深度优先搜索

更新一个模板:

这个模板可以不用回溯数据

import java.util.*;

public class Main {
	static int[] arr;
	static boolean[] b;
	static int n = 0;
	static int ok;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int nn = sc.nextInt();
		while (nn-- > 0) {
			n = sc.nextInt();
			arr = new int[n];
			b = new boolean[n];
			
			}
		}
	}

	public static void dfs(int one, int k, int bd,int sum) {
		if (sum == one) {
			bd++;
			k = 0;
			sum = 0;
		}
		if (bd == 3) {
			ok = 1;
			return;
		}
		for (int i = k; i < n; i++) {
			if (b[i] == false & sum + arr[i] <= one) {
				b[i] = true;
				dfs(one, i, bd,sum +arr[i]);
				b[i] = false;
				if (ok == 1) {
					return;
				}
			}
		}

	}
}

 

 DFS求迷宫路径

DFS(Depth-First-Search,深度优先搜索),顾名思义总是选择深度大的节点去访问,下面的图是一个二叉树,如果从头结点F开始深度优先遍历,若访问了C则下一个节点不可能是E,因为C和E的深度是一样的,违反深度优先原则。深度优先遍历序列不唯一。

例题:

迷宫问题一定一定要标记起点

可以把F想象成零点,C和E想象成上下左右的点,根据题目要求依次搜寻每一条路

从(0,0)开始向下搜索,每一个点又有四个点可以搜索,搜索完就返回到上一个节点,知道所有方案都搜索一遍。

这个题目其实是找从入口(Entrance)到出口(Exit)的可能的路径。矩阵(二维数组)从左上角开始,坐标为(0,0),可以向右走,坐标为(0,1);或者向下走,坐标为(1,0)。对于一般的位置(x,y),可以有4个搜索方向:右(x,y+1),下(x+1,y),左(x,y-1),上(x-1,y)。

根据模板写出:

void dfs()//参数用来表示状态  
{  
    if(到达终点状态)  
    {  
        ...//根据题意添加  
        return;  
    }  
    if(越界或者是不合法状态)  
        return;  
    if(特殊状态)//剪枝
        return ;
    for(扩展方式)  
    {  
        if(扩展方式所达到状态合法)  
        {  
            修改操作;//根据题意来添加  
            标记;  
            dfs();  
            (还原标记);  
            //是否还原标记根据题意  
            //如果加上(还原标记)就是 回溯法  
        }  
 
    }  
}

1.定义dfs函数:

2.设计返回条件:

3.判断是否到达出口

4.回溯,对于arr[x][y],走过的点设置为障碍,因为每个点都要上下左右搜寻一下,如果搜到来之前那个点,就会返回到之前那个点,成为了死循环,所以要将走过的路设为障碍,搜索完这条路之后将点的值返还!

如果此路不同,还有将path的值返还!

完整代码:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
	static String path = "";
	public static void dfs(int x, int y, int[][] arr, int endx, int endy,List<Integer>lists) {
		int lengthx = arr.length;
		int lengthy = arr[0].length;
		//判断是不是到达边界
		if (x >= arr.length | y >= arr[0].length) {
			return;
		}
		if (x < 1 | y < 1) {
			return;
		}
		//如果障碍
		if (arr[x][y] == 0) {
			return;
		}
		//如果到达了指定位置
		if (x == endx && y == endy) {
			lists.add(1);
			path = path + "(" + x + "," + y + ")";
			System.out.println(path);
			return;
		}
		//暂存path
		String temp = path;
		//加上此时的x,y
		path = path + "(" + x + "," + y + ")->";
		arr[x][y] = 0;
		dfs(x, y - 1, arr, endx, endy,lists);
		dfs(x - 1, y, arr, endx, endy,lists);
		dfs(x, y + 1, arr, endx, endy,lists);
		dfs(x + 1, y, arr, endx, endy,lists);
		arr[x][y] = 1;
		path = temp;
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int x = sc.nextInt();
		int y = sc.nextInt();
		int[][] arr = new int[x + 1][y + 1];
		for (int i = 1; i < arr.length; i++) {
			for (int j = 1; j < arr[0].length; j++) {
				arr[i][j] = sc.nextInt();
			}
		}
		List<Integer>lists =  new ArrayList<Integer>();
		int startx = sc.nextInt();
		int starty = sc.nextInt();
		int endx = sc.nextInt();
		int endy = sc.nextInt();
		dfs(startx, starty, arr, endx, endy,lists);
		if (lists.size()==0) {
			System.out.println("-1");
		}
		
	}
}

 

迷宫需要注意的相关问题:

给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

题目描述

输入格式

第一行N、M和T,N为行,M为列,T为障碍总数。第二行起点坐标SX,SY,终点坐标FX,FY。接下来T行,每行为障碍点的坐标。

输出格式

给定起点坐标和终点坐标,问每个方格最多经过1次,从起点坐标到终点坐标的方案总数。

输入输出样例

输入 #1

2 2 1
1 1 2 2
1 2

输出 #1

1

简单迷宫问题:

代码实现:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
	static boolean[][] arr = new boolean[1003][1003];
	static int dx[] = { 0, 0, 1, -1};
	static int dy[] = { 1, -1, 0, 0};
	static int n = 0;
	static int m = 0;
	static int t = 0;
	static int x1 = 0;
	static int y1 = 0;
	static int x2 = 0;
	static int y2 = 0;
	static int max = 0;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		t = sc.nextInt();
		x1 =  sc.nextInt();
		y1 = sc.nextInt();
		x2 = sc.nextInt();
		y2 = sc.nextInt();
		for (int i = 0; i < t; i++) {
			int xx = sc.nextInt();
			int yy = sc.nextInt();
			arr[xx][yy]=true;
		}
		dfs(0,x1,y1);
		System.out.println(max);
	}

	public static void dfs(int index,int i,int j) {
		if(i==x2&j==y2) {
			max++;
			return;
		}
		
		for (int k = 0; k < 4; k++) {
			int x = dx[k]+i;
			int y = dy[k]+j;
			if(x>n|y>m|x<1|y<1) {
				continue;
			}
			if(arr[x][y]==true) {
				continue;
			}
			arr[x][y]=true;
			dfs(index+1,x,y);
			arr[x][y]=false;
		}
	}
}

 评测:

心情可谓是非常难受了,为什么!!!!!!

我的思路明明不是跟第一篇思路一样吗为什么不给过!!!,只不过优化了很多,变成了数组方向!!!

分析:

第一篇与第二篇最大的区别是第一篇将起点标记了。

//因为dfs函数里并没有将起点设为已访问
       
//所以在后面的访问里,可能访问起点许多次
        //所以我的答案可能比标准答案多

纠正:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
	static boolean[][] arr = new boolean[1003][1003];
	static int dx[] = { 0, 0, 1, -1};
	static int dy[] = { 1, -1, 0, 0};
	static int n = 0;
	static int m = 0;
	static int t = 0;
	static int x1 = 0;
	static int y1 = 0;
	static int x2 = 0;
	static int y2 = 0;
	static int max = 0;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		t = sc.nextInt();
		x1 =  sc.nextInt();
		y1 = sc.nextInt();
		x2 = sc.nextInt();
		y2 = sc.nextInt();
		for (int i = 0; i < t; i++) {
			int xx = sc.nextInt();
			int yy = sc.nextInt();
			arr[xx][yy]=true;
		}
		arr[x1][y1]=true;//这就是许多人(我)70分的原因
	    //因为dfs函数里并没有将起点设为已访问
	    //所以在后面的访问里,可能访问起点许多次
	    //所以你的答案可能比标准答案多
		dfs(0,x1,y1);
		System.out.println(max);
	}

	public static void dfs(int index,int i,int j) {
		if(i==x2&j==y2) {
			max++;
			return;
		}
		
		for (int k = 0; k < 4; k++) {
			int x = dx[k]+i;
			int y = dy[k]+j;
			if(x>n|y>m|x<1|y<1) {
				continue;
			}
			if(arr[x][y]==true) {
				continue;
			}
			arr[x][y]=true;
			dfs(index+1,x,y);
			arr[x][y]=false;
		}
	}
}

重点:不要忘记将起点标记!!!!!

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    static boolean[][] arr = new boolean[1003][1003];
    static int dx[] = { 0, 0, 1, -1};
    static int dy[] = { 1, -1, 0, 0};
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < t; i++) {
            int xx = sc.nextInt();
            int yy = sc.nextInt();
            arr[xx][yy]=true;
        }
        arr[x1][y1]=true;//这就是许多人(我)70分的原因
        //因为dfs函数里并没有将起点设为已访问
        //所以在后面的访问里,可能访问起点许多次
        //所以你的答案可能比标准答案多
      
 dfs(0,x1,y1);
  
    }

 

 

类似迷宫题:

题目描述

有一个n*m的棋盘(1<n,m<=400),在某个点上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步

输入格式

一行四个数据,棋盘的大小和马的坐标

输出格式

一个n*m的矩阵,代表马到达某个点最少要走几步(左对齐,宽5格,不能到达则输出-1)

输入输出样例

输入 #1

3 3 1 1

输出 #1

0    3    2    
3    -1   1    
2    1    4    

这道题做的也是非常悔恨了,感觉为什么自己这么笨,每次做题总感觉离成功差那么一点点,可能这么一点点需要很多的题目来弥补吧!!!

题目思路:

根据跳马的规则,向四面八方跳,写成方向数组,然后搜索每一条路,把没到过的点赋值上到达时的步数。

关键点:

此题关键就是,在赋完值后,可能之后又会遍历到这个点,如果这个点值大于了此时的步数,那么以当前步数再次遍历这个点,如果这个点已经被遍历过并且这个点的值小于当且步数,则略过这个点,找下一个点

疑惑点:

阈值没搞明白咋出来的

import java.util.*;

public class Main {
	static int[][] a = new int[1000][1000];
	static boolean[][] b = new boolean[1000][1000];
	static int[] dx = { 1, 2, 1, 2, -1, -2, -2, -1 };
	static int[] dy = { 2, 1, -2, -1, 2, 1, -1, -2 };
	static int n = 0;
	static int m = 0;
	static int x = 0;
	static int y = 0;
	static int min = 999999;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		m = sc.nextInt();
		x = sc.nextInt();
		y = sc.nextInt();
		b[x][y] = true;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (i == x & j == y) {
					continue;
				} else {
					a[i][j] = -1;
				}
			}
		}
		dfs(1, x, y);
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (a[i][j] == 0) {
					if (i == x & j == y) {
						System.out.printf("%-5d", 0);
					} else {
						System.out.printf("%-5d", -1);
					}
				} else {
					System.out.printf("%-5d", a[i][j]);
				}
			}
			System.out.println();
		}
	}

	public static void dfs(int index, int x1, int y1) {

		if (index > 200) {//阈值 150 到200之前
			return;
		}
		for (int k = 0; k < 8; k++) {
			int x = dx[k] + x1;
			int y = dy[k] + y1;
			if (x > n | y > m | x < 1 | y < 1) {
				continue;
			}
			if (b[x][y] == true) {
				continue;
			}
			if (a[x][y] == -1 | a[x][y] > index + 1) {
				b[x][y] = true;
				a[x][y] = index;
				dfs(index + 1, x, y);
				b[x][y] = false;
			}
		}
	}

}

就差那一个小条件没想到,愁人!哭了

二:

DFS实现全排列(1,2,3)(重复):

全排列其实就是深搜,把每一种情况都要找到,然后输出出来。将其想像成组织图,从1开始往下搜,再从2,再从3,知道搜完全部的路

代码实现:

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int []a = new int[4];
		dfs(1,a);
	}	
public static void dfs(int index,int[]arr) {
	if(index == 4) {//临界条件,输出
		for (int i = 1; i < arr.length; i++) {
			System.out.print(arr[i]+" ");
		}
		System.out.println();
		return;
	}
	for (int i = 1; i <=3; i++) {
		arr[index]=i;
		dfs(index+1,arr);//让参数加一,知道调出3个数
	}
	
	}
}

 分析:

此全排列简单,不需要记住每个数走没走过,只要有路口就走,直到所有路口走完


 元素可重复,但是每个元素重复的个数有限

全排列带重复元素问题:

题目描述

设R={r1,r2,……,rn}是要进行排列的n个元素。其中元素r1,r2,……,rn可能相同。使设计一个算法,列出R的所有不同排列。

给定n以及待排列的n个元素。计算出这n个元素的所有不同排列。

输入格式

第1行:元素个数n(1<=n<500)

第2行:一行字符串,待排列的n个元素

输出格式

计算出的n个元素的所有不同排列,最后一行是排列总数。

输入输出样例

输入 #1

4
aacc

输出 #1

aacc
acac
acca
caac
caca
ccaa
6

这题刚拿来做的时候,直接用的其他全排列的方法,就是遍历每一个索引,然后将其标记,然后以该点的节点再次向下搜索,结果全部超时

分析:

分析一下发现,如果按照遍历索引展开搜索。很有可能造成很多很多的重复搜索 ,因为有很多重复的元素,从而导致超时。比如aaaabbcc,如果按照搜因肯定是以 a开头搜索,再以a开头,四次之后再轮到b,就会产生很多很多的重复答案,所以不能按照索引,按照元素 直接搜索 a,b,c

所以我们必须得想出一种方法,就是搜索过的相同节点不去搜索

所以我们不按照索引去搜索,我们按照元素去搜索,原因是按照索引去搜索,会搜索到相同元素,但是按照元素的不同去搜索,搜索不到重复元素

举例:aabbbcc

我们将问题理解为 搜索 a b c ,在一次排列中a必须用两次,b必须用3次,c必须用两次,所以当a用完两次后再次搜索到a就会略过。

搜索a  b c ,然后将每一个元素有多少个存放在一个数组当中。

代码分析:

1.

                  for (int i = 0; i < chars.length; i++) {
			a[(int) chars[i] - 97]++;
		}

计数

2.

                      for (int j = 0; j < 26; j++) { a b c
			if (a[j] > 0) {  判断只有当个数大于0的时候
				a[j]--;
				String strk = strs.toString();
				strs.append(String.valueOf((char) (97 + j)));
				dfs(chars, index + 1);
				strs = new StringBuffer(strk);
				a[j]++; 回溯
			}
		}

 

 具体代码实现:

import java.util.*;

public class Main {
	static int n = 0;
	static boolean[] b = new boolean[1000];
	static int arr[][] = new int[1000][1000];

	static int num = 0;
	static int[] a = new int[1000];
	static String str = "";
	static StringBuffer strs = new StringBuffer("");
	static List<String> lists = new ArrayList<String>();
	static List<String> listf = new ArrayList<String>();

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		sc.nextLine();
		str = sc.nextLine();
		char[] chars = str.toCharArray();
		for (int i = 0; i < chars.length; i++) {
			a[(int) chars[i] - 97]++;
		}
		Arrays.sort(chars);
		dfs(chars, 0);
		System.out.println(num);
	}

	public static void dfs(char[] chars, int index) {
		if (index == chars.length) {
			System.out.println(strs);
			num++;
			return;
		}
		for (int j = 0; j < 26; j++) {
			if (a[j] > 0) {
				a[j]--;
				String strk = strs.toString();
				strs.append(String.valueOf((char) (97 + j)));
				dfs(chars, index + 1);
				strs = new StringBuffer(strk);
				a[j]++;
			}
		}
	}

}

无重复:

import java.util.*;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int[] a = new int[4];
		boolean[] arr = new boolean[4];// 初始默认值是flase,用来判断是否用过次标记,如果用过就让其等于true
		dfs(1, a, arr);
	}

	public static void dfs(int index, int[] a, boolean[] arr) {

		if (index == 4) {//等于临界条件
			for (int i = 1; i < a.length; i++) {
				System.out.print(a[i] + " ");
			}
			System.out.println();
			return;
		}
		for (int i = 1; i <= 3; i++) {
			if (arr[i] != true) {//如果这个数没被用过
				a[index] = i;
				arr[i]=true;//这个数被标记上,表示用过
				dfs(index + 1, a, arr);
				arr[i]=false;//回溯
			}

		}
	}
}

 分析:无重复全排列,重点在与走过点选择抛弃,不能再走,对于这个问题,创建一个布尔数组,对走过的点也就是i标记,走过的路标记为true,重点是当用完这个点回来的时候要将其初始化,也就是回溯

举个例子:当经过1时,1从false变成了true,当1 3 2输出后,这个数组的头(1)变成2的,此时以2打头向下选,如果1没有被初始化成flase,还是ture的话,2就直接找到了3,而不是1

 原方案:先保存,再输出,超时

public static void dfs(int index) {
		if(index==str.length()+1) {
			jj++;
			for (int i = 1; i < index; i++) {for循环遍历输出
				System.out.print(chark[i]);
			}
			System.out.println();
			return;
		}
		for (int i = 1; i <= 26; i++) {
			if (arr[i] > 0) {
				chark[index] = (char) (i + 96);将每一个元素保存
				arr[i]--;
				dfs(index + 1);
				arr[i]++;
			}
		}
	}
}

优化:通过

	public static void dfs(int index) {
		if (index == str.length() + 1) {
			jj++;
			System.out.println(strs);直接输出
			return;
		}
		for (int i = 1; i <= 26; i++) {
			if (arr[i] > 0) {
				arr[i]--;
				String strk = strs.toString();
				strs.append(String.valueOf((char) (i+96)));在搜索中加到字符串中
				dfs(index + 1);
				strs = new StringBuffer(strk);回溯
				arr[i]++;
			}
		}
	}


无重复并且无123 321的:

import java.util.Scanner;

public class Main2 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int[] arr = new int[5];
		for (int i = 1; i < arr.length; i++) {
			arr[i] = sc.nextInt();
		}
		int[] b = new int[5];
		int[] a = new int[5];
		dfs(arr, 1, b, a);
	}

	public static void dfs(int[] arr, int index, int[] b, int[] a) {
		if (index+1 == arr.length) {
			for (int i = 1; i <=3; i++) {
				System.out.print(a[i]+" ");
			}
			System.out.println();
			return;
		} else {
			for (int i = 1; i < arr.length; i++) {
				if (i > b[index - 1]) {
					a[index] = arr[i];
					b[index] = i;
					dfs(arr, index + 1, b, a);
					
				}
			}
		}

	}
}

 分析:得到此排列关键步骤就是if判断,当前的i值必须大于上一个数的i值,此时就需要一个数组来保存上一个i值,进行比较,才能往下寻找这样就不会有321 和123 这样的反复数组

优化方案:(常用)

	                   for (int i = ii+1; i <= m; i++) {
				a[index]=arr[i];
				dfs(index+1,i);
		}

直接将下一次循环的起点定在上一个数之后,会缩短运行时间!


 

如果我想通过一次深搜,挑出搜索长度在1到n的序列,比如 1,12,123

	public static void dfs(int k, int index) {

		for (int i = k; i <= n; i++) {从下一个数开始,优化
			a[k] = i;找到一个数
			for (int j = 1; j <= index; j++) {
				System.out.print(a[j] + " ");
			}
			System.out.println();
			dfs(i + 1, index + 1);

		}
	}

实现:长度在1到n的序列全部找出

例题实践:

 从题目中我们知道,就是让我们搜索到一种组合让suma和sumb的差值最小。这个时候我们用到这种任意长度序列搜索,因为没告诉我们必须挑几件事干,可能是第一件事和第二件事,也有可能是只有第一件事

代码实现:


import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;

public class Hdu1231 {
	static int n = 0;
	static int v = 0;
	static int[][] arr = new int[35][35];
	static int a[] = new int[50];
	static int suma = 0;
	static int sumb = 0;
	static int abs = 99999999;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		v = sc.nextInt();
		for (int i = 1; i <= n; i++) {
			arr[i][1] = sc.nextInt();
			arr[i][2] = sc.nextInt();
		}
		dfs(1);
		if (abs == 99999999) {
			System.out.println(-1);
		} else {
			System.out.println(abs);
		}
	}

	public static void dfs(int index) {
		if(abs==0) {
			return;
		}
		if (suma + sumb >= v) {每到一个节点判断一次
			abs = Math.min(abs, Math.abs(suma - sumb));
		}

		for (int i = index; i <= n; i++) {
			suma += arr[i][1];
			sumb += arr[i][2];
			dfs(i + 1);
			suma -= arr[i][1];
			sumb -= arr[i][2];

		}
	}
}

带有杨辉三角性质的题目:

1.例题:

到了Pizza Hut,爱与愁大神由于不爽,所以存心想坑月落乌啼的钱,ta点了m样菜,每样菜ai元。月落乌啼预计只用n元,于是他让爱与愁大神重新从这m样菜中选r样。爱与愁大神还是想坑钱,于是ta打电话给你,让你编一个程序告诉ta有几种方案可以从m样菜中点取r样菜但是还能超过月落乌啼的预计n元。

输入格式

第1行:三个数 m,r,n。

第2行:m个数,每道菜需要的钱ai,两个数之间有空格。

输出格式

只有一个整数,表示方案总数。

输入输出样例

输入 #1

5 2 8

1 7 2 5 4

输出 #1

4

题目思路:

就是从m中挑r个数,这r个数的和要大于n,其实就是无反复重复的全排列,但是需要剪枝!

剪枝一:如果当前已经选出的菜的钱数f>nf>nf>n,那么就说明接下来不管怎么选都是可行的,根据组合数的性质(杨辉三角):

所以根据地推将值预先处理出来:

                     c[0][0]=1;
		    for(int i=1;i<=m;i++){
		        c[i][0]=1;
		        for(int j=1;j<=i;j++) {
		            c[i][j]=c[i-1][j]+c[i-1][j-1];
		    }
		}

 剪枝二:从后面的菜选

		for(int i=ii+1;i<=m-r+index;i++) {
			sum+=arr[i];
			dfs(index+1,i);
			sum-=arr[i];
	}

代码:

import java.util.*;

public class Main {
	static int m = 0;
	static int r = 0;
	static int n = 0;
	static int[]arr = new int[103];
	static int[] b = new int[103];
	static int num = 0;
	static int sum = 0;
	static int c[][] = new int[103][103];
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner sc = new Scanner(System.in);
			m = sc.nextInt();
			r = sc.nextInt();
			n = sc.nextInt();
			for (int i = 1; i <= m; i++) {
				arr[i]=sc.nextInt();
			}
			c[0][0]=1;
		    for(int i=1;i<=m;i++){
		        c[i][0]=1;
		        for(int j=1;j<=i;j++) {
		            c[i][j]=c[i-1][j]+c[i-1][j-1];
		    }
		}
			dfs(1,0);
			System.out.println(num);
	}

	public static void dfs(int index,int ii) {
		
		if(index==r+1||sum>n) {
			if(sum>n) {
				num+=c[m-ii][r-(index-1)];
			}
			return;
		}
		for(int i=ii+1;i<=m-r+index;i++) {
			sum+=arr[i];
			dfs(index+1,i);
			sum-=arr[i];
	}
		
		
	}
	
}

2.

现在给出N和最后一个数sum,求字典序最小的一个序列。

题目思路:

最开始我是找出所有的全排列,看哪个符合条件,得了七十分,超时三个点

想要AC此题,就必须搞懂这个三角形的本质究竟是神马。

大家可以算一算,设一个较小的数,模拟一下。

比如偶设n=5,按照题目中的要求一步步模拟:

a     b     c     d      e
  a+b   b+c   c+d     d+e
   a+2b+c b+2c+d  c+2d+e
    a+3b+3c+d b+3c+3d+e
        a+4b+6c+4d+e

好,最终我们通过一步步的模拟,算出当n=5时,1~5位上对应的权值应该是:

位数 1 2 3 4 5
权值 1 4 6 4 1

看到这很激动——这不就是杨辉三角的第五行么!!!

所以结论已经很明显,系数已经给出,每找一个数乘相应系数,找n个数,看是否符合sum,必须先将系数处理出来:

                            c[1][1] = 1;
		for (int i = 2; i <= n; i++) {
			c[i][1] = 1;
			for (int j = 2; j <= n; j++) {
				c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
			}
		}

剪枝:

如果sum大于k直接返回

全部代码:

import java.math.BigDecimal;
import java.util.*;

public class Main2 {
	static int n = 0;
	static int k = 0;
	static int sum = 0;
	static boolean[] b = new boolean[20];
	static int[] arr = new int[20];
	static int[] arrs = new int[20];
	static int c[][] = new int[20][20];
	static StringBuffer str = new StringBuffer("");
	static int kb = 0;

	public static void main(String[] args) {
		// TODO Auto-generated me
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值