欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

例題詳解Java?dfs與記憶化搜索和分治遞歸算法的使用

 更新時(shí)間:2022年04月08日 13:58:20   作者:撩得Android一次心動(dòng)  
遞歸指函數(shù)調(diào)用自身。常用的遞歸算法有dfs(深度優(yōu)先搜索)、記憶化搜索和分治,接下來(lái)將用幾個(gè)算法題來(lái)帶你熟練掌握它

一、dfs(深度優(yōu)先搜索)

1.圖的dfs

/**
 * 深度優(yōu)先搜索
 * 
 * @param node
 * @param set
 */
public void DFS(Node node, Set<Node> set) {
    if (node == null) {
        //當(dāng)沒(méi)有節(jié)點(diǎn)時(shí),退出此次方法
        return;
    }
    if (!set.contains(node)) {
        //沒(méi)有搜到過(guò)就保存,并且繼續(xù)向下推進(jìn)
        set.add(node);
        System.out.print(node.value + " ");
        for (Node node1 : node.nexts) {
            DFS(node1, set);
        }
    }
}

2.樹(shù)的dfs

樹(shù)(邊數(shù)=頂點(diǎn)數(shù)-1的圖)的dfs

public static void dfs(Node treeNode) {
        if (treeNode == null) {
            return;
        }
        System.out.print(treeNode.value + " ");
        // 遍歷左節(jié)點(diǎn)
        dfs(treeNode.left);
        // 遍歷右節(jié)點(diǎn)
        dfs(treeNode.right);
    }

使用dfs代替狀壓枚舉:  dfs 優(yōu)于 狀壓枚舉

二、記憶化搜索

記憶化搜索,指通過(guò)記錄已經(jīng)搜索到的值來(lái)降低重復(fù)操作次數(shù),從而加快運(yùn)行速度。

斐波那契數(shù)列問(wèn)題: 1,1,2,3,5,8,... 求斐波那契數(shù)列第n項(xiàng)

1.普通遞歸:O(2^n)

public static int f(int n){
    if(n<=1)return 1;
    return f(n-1)+f(n-2);
}

2.記憶化搜索: O(n)

    static int[] dp = new int[1000] ;
    public static int f1(int n){
        if(n<=1) return dp[n] = 1;
        if(dp[n]!=0)return dp[n];
        return dp[n]=f1(n-1)+f1(n-2);
    }

三、分治

將問(wèn)題拆分成子問(wèn)題進(jìn)行求解。

例如歸并排序: 1,4,7,2,5,8,3,6,9

第一步 拆分: 左邊:1,4,7,2 右邊:5,8,3,6,9

第二步 遞歸排序 左邊:1,2,4,7 右邊:3,5,6,8,9

第三步 合并兩個(gè)有序數(shù)組 左邊:1,2,4,7 右邊:3,5,6,8,9

四、算法題

1.dia和威嚴(yán) 

難度??

知識(shí)點(diǎn):dfs

從根開(kāi)始dfs即可,dfs的過(guò)程中就能找到每個(gè)點(diǎn)需要的時(shí)間,維護(hù)一下最大值。

題目描述:

dia是學(xué)生會(huì)長(zhǎng)。她經(jīng)常有信息要傳達(dá)給別人。

學(xué)生會(huì)的階層等級(jí)森嚴(yán)。每個(gè)階級(jí)傳達(dá)信息只能傳達(dá)到自己的下屬。

一個(gè)人可以有多個(gè)下屬,但一個(gè)人最多只能有一個(gè)上級(jí)。(樹(shù))

dia作為學(xué)生會(huì)長(zhǎng),很明顯是沒(méi)有上級(jí)的(即全學(xué)生會(huì)權(quán)利最大者)。

已知每個(gè)人傳達(dá)到下屬所消耗的時(shí)間。(傳達(dá)給不同的下屬,時(shí)間不一定相同) 現(xiàn)在dia有一個(gè)信息要傳達(dá)給一個(gè)指定者。只有信息接收的指定者才需要理解信息(花費(fèi)ai的時(shí)間)。她不想讓效率太慢,于是她想找出最大時(shí)間消耗的那個(gè)路線(xiàn)(包括信息接收指定者所需要理解信息的時(shí)間),這樣就能方便整改。

輸入描述:

第一行一個(gè)正整數(shù)n,代表學(xué)生會(huì)的人數(shù)(dia是1號(hào),其他人標(biāo)記為2號(hào)到n號(hào))。 (1≤n≤200000)第二行有n-1個(gè)正整數(shù)ai,代表從2號(hào)到n號(hào),每個(gè)人需要理解信息的時(shí)間。 (1≤a1≤100)接下來(lái)的n-1行,每行有3個(gè)正整數(shù)x,y,k,代表x是y的上級(jí),x向y傳播信息需要消耗k的時(shí)間。(1≤x,y≤n,1≤k≤100)

輸出描述:

一個(gè)正整數(shù),代表dia選定某人作為信息接收指定者后,花費(fèi)總時(shí)間的最大值。

示例

輸入

3

3 4

1 2 4

1 3 2

輸出

7

說(shuō)明

若dia指定3號(hào)為信息接受者,消耗時(shí)間為2+4=6。

若dia指定2號(hào)為信息接受者,消耗為4+3=7。

故最大值為7。

備注:

注:只有指定者需要理解信息,傳達(dá)者不需要花費(fèi)時(shí)間理解信息。

import java.util.*;
import java.io.*;
public class Main{
    static class Edge{
        int to;
        int weight;
        //保存子節(jié)點(diǎn),與線(xiàn)權(quán)(傳播時(shí)間)。
        Edge(int to,int weight){
            this.to = to;
            this.weight = weight;
        }
    }
    static ArrayList<Edge>[] g;
    static int maxtime;
    static int weight[];
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine()); //n為總?cè)藬?shù)
        weight = new int[n+1];
        g = new ArrayList[n+1];
        // 每個(gè)桶中保存一個(gè)ArrayList(可達(dá))
        //這里的i代表了第幾個(gè)數(shù)(從1開(kāi)始)
        for(int i = 1;i<=n;i++){
            g[i] = new ArrayList();
        }
        //保存所有點(diǎn)權(quán)(理解時(shí)間)紀(jì)錄了從2到n的點(diǎn)權(quán),與數(shù)字的位子相對(duì)應(yīng)。
        String[] s = br.readLine().split(" ");
        for(int i = 0;i<n-1;i++){
            weight[i+2] = Integer.parseInt(s[i]);
        }
        for(int i = 0;i<n-1;i++){
            String[] s1 = br.readLine().split(" ");
            int x = Integer.parseInt(s1[0]);
            int y = Integer.parseInt(s1[1]);
            int z = Integer.parseInt(s1[2]);
            g[x].add(new Edge(y,z));
        }
        dfs(1,0);
        System.out.println(maxtime);
    }
    static void dfs(int i ,int time){
        if(maxtime<time+weight[i])maxtime = time+weight[i];
        for(int k = 0;k<g[i].size();k++){
            dfs(g[i].get(k).to,time+g[i].get(k).weight);
        }
    }
}

非常典型的dfs模板題目:dfs一個(gè)樹(shù),尋找根到某點(diǎn)的線(xiàn)權(quán)總合+某點(diǎn)的點(diǎn)權(quán)

思考:???

思考1:假如變成根到某點(diǎn)的線(xiàn)權(quán)總合+根到某點(diǎn)的點(diǎn)權(quán)總合,程序應(yīng)該怎么改?

解:

dfs(g[i].get(k).to,time+g[i].get(k).weight+weight[i]);//那就加個(gè)點(diǎn)權(quán)

思考2:假如變成根到葉子點(diǎn)的線(xiàn)權(quán)總合+葉子點(diǎn)的點(diǎn)權(quán),程序應(yīng)該怎么改?

(1)首先思考一個(gè)問(wèn)題:這個(gè)遞歸沒(méi)有任何判斷結(jié)束的條件。他是怎么結(jié)束的?

        因?yàn)槲覀儗⑺f歸的過(guò)程安排的明明白白,他只是在執(zhí)行有序有限的遞歸操作。所以沒(méi)有給出判斷條件。

(2)那么我們應(yīng)該怎么判斷他到葉子節(jié)點(diǎn)了呢?

        我們單單在遞歸的代碼上修改是不能判斷出是否到達(dá)了葉子節(jié)點(diǎn),所以還需要想清楚結(jié)構(gòu)體是咋樣的。

解:

if(maxtime<time+weight[i]&&g[i].isEmpty())maxtime = time+weight[i];

思考3:假如變成隨意定點(diǎn)到某點(diǎn)的線(xiàn)權(quán)總合+某點(diǎn)的點(diǎn)權(quán),程序應(yīng)該怎么改?

解:

dfs(點(diǎn)的編號(hào),0); 0代表時(shí)間從0開(kāi)始遞歸

2.小紅點(diǎn)點(diǎn)點(diǎn) 

難度??

知識(shí)點(diǎn):dfs

開(kāi)一個(gè)visited數(shù)組用來(lái)標(biāo)記是否訪(fǎng)問(wèn)。對(duì)于每個(gè)沒(méi)被訪(fǎng)問(wèn)過(guò)的紅色節(jié)點(diǎn),開(kāi)始dfs并標(biāo)記其相鄰的紅色節(jié)點(diǎn)。只要標(biāo)號(hào)是從小到大開(kāi)始遍歷,最終形成的方案就一定是字典序最小的

題目描述:

小紅拿到了一個(gè)圖,其由 n個(gè)頂點(diǎn)、m 條邊組成。

這個(gè)圖上面的一些點(diǎn)被染成了紅色。

小紅有一個(gè)能力:她每次可以點(diǎn)擊一個(gè)紅色的頂點(diǎn),并將和這個(gè)頂點(diǎn)相鄰的紅色連通塊的所有紅色頂點(diǎn)全部標(biāo)記。

當(dāng)兩個(gè)紅色頂點(diǎn)有一條邊相連時(shí),這兩個(gè)紅色頂點(diǎn)被稱(chēng)為連通的。另外,若a和b連通且b和c連通,那么a和c也是連通的。

小紅想知道自己至少要點(diǎn)擊多少次,可以把所有紅色的頂點(diǎn)全部標(biāo)記。

你能告訴她點(diǎn)擊的次數(shù)嗎?并請(qǐng)你輸出一個(gè)點(diǎn)擊的方案,如果有多個(gè)方案合法,請(qǐng)你輸出一個(gè)字典序最小的方案。

注:字典序的定義:兩個(gè)不同方案的字典序比較:對(duì)于從左到右數(shù)第一個(gè)不同的數(shù),哪個(gè)方案最小,那么它的字典序最小。

例如:方案[1,4,5]和方案[1,3,6]相比,后者更小。因?yàn)榈谝粋€(gè)出現(xiàn)的不同的數(shù)是第二個(gè)數(shù),4>3。

輸入描述:

第一行輸出兩個(gè)正整數(shù) n 和 m ,用空格隔開(kāi)。分別代表頂點(diǎn)數(shù)和邊數(shù)。

第二行輸入一個(gè)長(zhǎng)度為 n 的字符串,代表每個(gè)頂點(diǎn)的染色情況。第 i 個(gè)字符為'R'代表第 i 個(gè)點(diǎn)被染成紅色,為'W'代表未被染色。

接下來(lái)的 m 行,每行兩個(gè)正整數(shù) x 和 y ,代表 x 和 y 有一條無(wú)向邊相連。

不保證圖是整體連通的。不保證沒(méi)有重邊和自環(huán)。

數(shù)據(jù)范圍:

輸出描述:

第一行輸出一個(gè)正整數(shù) cnt ,代表小紅的點(diǎn)擊次數(shù)。

第二行輸出 cnt 個(gè)正整數(shù),用空格隔開(kāi),代表小紅點(diǎn)擊的順序。

示例1

輸入

5 5

RRRWR

3 4

3 1

2 5

5 5

4 5

輸出

2

1 2

說(shuō)明

可以發(fā)現(xiàn),共有2個(gè)紅色連通塊:{1,3}和{2,5}。只需要點(diǎn)擊2次即可,字典序最小的方案是[1,2]

import java.util.*;
import java.io.*;
 
public class Main {
    static ArrayList<Integer>[] g;
    static String[] strings;
    static int[] visited;
 
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] firstLine = br.readLine().split(" ");
        int n = Integer.parseInt(firstLine[0]);
        int m = Integer.parseInt(firstLine[1]);
        g = new ArrayList[n+1];
        visited = new int[n+1];
        for (int i=1;i<n+1;i++) {
            g[i] = new ArrayList<Integer>();
        }
        //一個(gè)字符一個(gè)字符的讀取
        strings = br.readLine().split("");
        for (int i=0;i<m;i++) {
            //描繪雙向圖
            String[] temp = br.readLine().split(" ");
            int x = Integer.parseInt(temp[0]);
            int y = Integer.parseInt(temp[1]);
            g[x].add(y);
            g[y].add(x);
        }
        int cnt = 0;
        StringBuilder sb = new StringBuilder();
        for (int i=1;i<n+1;i++) {
            if (visited[i] ==0 && strings[i-1].equals("R")) {
                cnt++;
                sb.append(i + " ");
                //從糖葫蘆小的開(kāi)始紀(jì)錄,然后延深。
                dfs(i);
            }
        }
        System.out.println(cnt);
        System.out.println(sb);
    }
 
    static void dfs(int x) {
        if (visited[x] ==0 && strings[x-1].equals("R")) {
            //點(diǎn)亮它
            visited[x] = 1;
            for (int i=0;i<g[x].size();i++) {
                dfs(g[x].get(i));
            }
        }
    }
}

3.kotori和素因子 

難度???

知識(shí)點(diǎn):dfs(回溯法)

按照題意進(jìn)行dfs即可。注意素因子的判重,可以使用set或者visited數(shù)組。

題目描述:

kotori拿到了一些正整數(shù)。她決定從每個(gè)正整數(shù)取出一個(gè)素因子。但是,kotori有強(qiáng)迫癥,她不允許兩個(gè)不同的正整數(shù)取出相同的素因子。

她想知道,最終所有取出的數(shù)的和的最小值是多少?

注:若a%k==0,則稱(chēng)k是a的因子。若一個(gè)數(shù)有且僅有兩個(gè)因子,則稱(chēng)其是素?cái)?shù)。顯然1只有一個(gè)因子,不是素?cái)?shù)。

輸入描述:

第一行一個(gè)正整數(shù)n,代表kotori拿到正整數(shù)的個(gè)數(shù)。

第二行共有n個(gè)數(shù)ai,表示每個(gè)正整數(shù)的值。

保證不存在兩個(gè)相等的正整數(shù)。

1<=n<=10

2<=ai<=1000

輸出描述:

一個(gè)正整數(shù),代表取出的素因子之和的最小值。若不存在合法的取法,則輸出-1。

示例1 

輸入

4

12 15 28 22

輸出

17

說(shuō)明

分別取3,5,7,2,可保證取出的數(shù)之和最小 

示例2

輸入

5

4 5 6 7 8

輸出

-1

備注:        1<=n<=10        2<=ai<=1000

import java.util.*;
import java.io.*;
 
public class Main{
    static boolean[] check = new boolean[2000];
    static int[] ai;
    static int n;
    static int min = Integer.MAX_VALUE;
    //判斷是否為素?cái)?shù)
    static boolean isPrime(int x){
        if(x<2) return false;
        //這是根據(jù)開(kāi)根號(hào)的演化版本,提高了效率。
        for(int i=2;i*i<=x;i++){
            if(x%i==0)return false;
        }
        return true;
    }
    static void dfs(int index,int sum){
        if(index==n){
            min=Math.min(min,sum);
            return;
        }
        //查找這個(gè)數(shù)沒(méi)有被占用的素因子。
        for(int i=2;i<=ai[index];i++){
            if(ai[index]%i==0&&check[i]==false&&isPrime(i)){
                check[i]=true;
                dfs(index+1,sum+i);
                //回溯的方法。
                check[i]=false;
            }
        }
    }
    public static void main(String[] args)throws IOException{ 
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        n = Integer.parseInt(br.readLine());
        String[] a = br.readLine().split(" ");
        //負(fù)責(zé)保存所有輸入的數(shù)
        ai = new int[n];
        for(int i=0;i<n;i++){
            ai[i]=Integer.parseInt(a[i]);
        }
        dfs(0,0);
        System.out.println(min==Integer.MAX_VALUE ? -1:min);
        br.close();
    }
}

4.kotori和糖果 

難度????

知識(shí)點(diǎn):記憶化搜索

正難則反,我們反向推理。

如果共有n個(gè)糖果:

  • 若n為偶數(shù),最后一次合并一定是兩堆n/2合并最優(yōu)。
  • 若n為奇數(shù),最后一次合并一定是一堆n/2和一堆n/2+1合并最優(yōu)(合并的價(jià)值為1)。

所以得到遞推式:f(n)=f(n/2)+f(n-n/2)+n%2

題目描述:

kotori共有n塊糖果,每塊糖果的初始狀態(tài)是分散的,她想把這些糖果聚在一堆。但她每次只能把兩堆糖果合并成一堆。

已知把兩堆數(shù)量為a和b的糖果聚在一堆的代價(jià)是|a-b|。

kotori想知道,她把這n塊糖果聚在一堆的最小代價(jià)是多少?

輸入描述:

第一行是一個(gè)正整數(shù)T,代表數(shù)據(jù)組數(shù)。

第二行到第T+1行,每行一個(gè)非負(fù)整數(shù)n,代表kotori的糖果總數(shù)量。

輸出描述:

每行一個(gè)整數(shù),代表kotori需要花費(fèi)的最小代價(jià)。

示例

輸入

2

5

6

輸出

2

2

說(shuō)明

n=5時(shí),kotori可以這樣聚集糖果:

1 1 1 1 1

2 1 1 1

2 2 1

2 3

5

每一步的代價(jià)分別是0,0,1,1,總代價(jià)為2。

備注:

對(duì)于50%的數(shù)據(jù),0<T≤100000, 0≤n≤100000

對(duì)于另外50%的數(shù)據(jù),T=1 , 0≤n≤1e18(這個(gè)數(shù)據(jù)范圍是為了為了讓你動(dòng)態(tài)規(guī)劃做不了,上面的范圍可以做)

import java.util.*;
import java.io.*;
public class Main{
    static HashMap<Long,Long> map = new HashMap<>();
    public static void main (String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int num = Integer.parseInt(br.readLine());
        for(int i = 0; i<num; i++){
            System.out.println(recurse(Long.parseLong(br.readLine())));
        }
    }
    static long recurse(long num){
        if(num <= 1) return 0;
        //用于保存
        if(map.containsKey(num)) return map.get(num);
        long pay = recurse(num/2) + recurse(num-num/2) +num%2;
        map.put(num,pay);
        return pay;
    }
}

num%2的作用就是為了計(jì)數(shù),且保存。

到此這篇關(guān)于例題詳解Java dfs與記憶化搜索和分治遞歸算法的使用的文章就介紹到這了,更多相關(guān)Java 遞歸算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringCloud_Eureka服務(wù)注冊(cè)與發(fā)現(xiàn)基礎(chǔ)及構(gòu)建步驟

    SpringCloud_Eureka服務(wù)注冊(cè)與發(fā)現(xiàn)基礎(chǔ)及構(gòu)建步驟

    Eureka服務(wù)注冊(cè)中心,主要用于提供服務(wù)注冊(cè)功能,當(dāng)微服務(wù)啟動(dòng)時(shí),會(huì)將自己的服務(wù)注冊(cè)到Eureka Server,這篇文章主要介紹了SpringCloud中Eureka的配置及詳細(xì)使用,需要的朋友可以參考下
    2023-01-01
  • Win10系統(tǒng)下配置Java環(huán)境變量

    Win10系統(tǒng)下配置Java環(huán)境變量

    今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Win10系統(tǒng)下配置Java環(huán)境變量展開(kāi),文中有非常詳細(xì)的介紹及圖文示例,需要的朋友可以參考下
    2021-06-06
  • Java面向?qū)ο蠡A(chǔ)知識(shí)之枚舉

    Java面向?qū)ο蠡A(chǔ)知識(shí)之枚舉

    這篇文章主要介紹了Java面向?qū)ο蟮闹杜e,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下
    2021-11-11
  • Springboot2 集成 druid 加密數(shù)據(jù)庫(kù)密碼的配置方法

    Springboot2 集成 druid 加密數(shù)據(jù)庫(kù)密碼的配置方法

    這篇文章給大家介紹Springboot2 集成 druid 加密數(shù)據(jù)庫(kù)密碼的配置方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-07-07
  • 淺析Java中XPath和JsonPath以及SpEL的用法與對(duì)比

    淺析Java中XPath和JsonPath以及SpEL的用法與對(duì)比

    XPath,即XML路徑語(yǔ)言,是一種用于在XML文檔中查找信息的語(yǔ)言,JsonPath是從XPath中發(fā)展而來(lái)的,專(zhuān)門(mén)用于JSON數(shù)據(jù)格式,本文主要來(lái)講講他們的用法與區(qū)別,需要的可以參考下
    2023-11-11
  • spring-boot-maven-plugin未指定版本導(dǎo)致的編譯錯(cuò)誤問(wèn)題

    spring-boot-maven-plugin未指定版本導(dǎo)致的編譯錯(cuò)誤問(wèn)題

    這篇文章主要介紹了spring-boot-maven-plugin未指定版本導(dǎo)致的編譯錯(cuò)誤問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • Java的Lombok之@Builder使用總結(jié)

    Java的Lombok之@Builder使用總結(jié)

    這篇文章主要介紹了Java的Lombok之@Builder使用總結(jié),當(dāng)不使用@Builder注解到類(lèi)上,創(chuàng)建T1的有參構(gòu)造函數(shù),入?yún)⒉粌H包括T1中所有的參數(shù),還包括T中所有的參數(shù),T2的屬性由T1在有參構(gòu)造函數(shù)中通過(guò)調(diào)用父類(lèi)構(gòu)造器的方式賦初值,需要的朋友可以參考下
    2023-12-12
  • Spring Boot中Bean定義方調(diào)用方式解析

    Spring Boot中Bean定義方調(diào)用方式解析

    這篇文章主要介紹了Spring Boot中Bean定義方調(diào)用方式解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • java生成二維碼并且給二維碼添加logo

    java生成二維碼并且給二維碼添加logo

    這篇文章主要介紹了java生成二維碼并且給二維碼添加logo的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • mybatisplus之Wrappers.ne踩坑記錄解決

    mybatisplus之Wrappers.ne踩坑記錄解決

    這篇文章主要為大家介紹了mybatisplus之Wrappers.ne踩坑記錄解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05

最新評(píng)論