dfs相关练习题

部分和

/*
 * 给定整数序列a1,a2,...,an,判断是否可以从中选出若干个数,使它们的和恰好为k
 * 输入:
 * n=4
 * a={1,2,4,7}
 * k=13
 * 输出:
 * Yes(13=2+4+7) 
 */
public class _部分和 {
	
	private static int kk;

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] a = new int[n];
		for(int i = 0;i < n;i++) {
			a[i] = sc.nextInt();
		}
		int k = sc.nextInt();
		kk = k;
		dfs(a,k,0,new ArrayList<Integer>());
	}

	public static void dfs(int[] a, int k, int cur, ArrayList<Integer> ints) {
		if(k == 0) {
			System.out.print("Yes("+kk+" = ");
			int size = ints.size();
			for(int i = 0;i < size;i++) {
				System.out.print(ints.get(i)+(i==size-1?"":" + "));
			}
			System.out.println(")");
			System.exit(0);
		}
		if(k < 0 || cur == a.length)
			return;
		//不要cur下标这个元素
		dfs(a,k,cur+1,ints);
		//要cur下标这个元素
		ints.add(a[cur]);
		int index = ints.size()-1;
		dfs(a,k-a[cur],cur+1,ints);
		ints.remove(index);//回溯
	}

}

机器人走方格

/*
 * 在一个X*Y的网格中,一个机器人只能走格点且只能向右或向下走,要从左上角走到右下角
 * 计算机器人有多少种走法
 * 给定两个整数int x,int y,返回机器人的走法数目,保证x+y小于等于12
 */
public class _机器人走方格 {

	public static void main(String[] args) {
		System.out.println(solve_1(3,3));
		System.out.println(solve_2(3,3));
	}

	//利用递归解决
	public static int solve_1(int i, int j) {
		if(i == 1 || j == 1)
			return 1;
		return solve_1(i,j-1) + solve_1(i-1,j);
	}

	//不用递归
	public static int solve_2(int i, int j) {
		int[][] arr = new int[i][j];
		for(int m = 0; m < j;m++) {
			arr[m][0] = 1;
		}
		for(int n = 0; n < i; n++) {
			arr[0][n] = 1;
		}
		for(int m = 1; m < i ;m++) {
			for(int n = 1;n < j; n++) {
				arr[m][n] = arr[m][n-1] + arr[m-1][n];
			}
		}
		return arr[i-1][j-1];
	}
}

困难的串

/*
 * 问题:如果一个字符串包含两个相邻的重复子串,则称它为容易的串,否则为困难的串
 * 如,BB,ABCDACABCAB,ABCDABCD是容易的串,D,DC,ABDAB,CBABCBA都是困难的串
 * 
 * 输入正整数n,L,输出由前L个字符组成的,字典序第n小的困难的串
 * 例如,当L=3时,前7个困难的串分别为:
 * A,AB,ABA,ABAC,ABACA,ABACAB,ABACABA
 * n指定为4时,输出ABAC
 */
public class _困难的串 {
	public static void main(String[] args) {
		int n = 10;
		int l = 4;
		dfs(l,n,"");
	}

	static int count;
	
	public static void dfs(int l, int n, String prefix) {
		//尝试在prefix后面追加一个字符
		for(char i = 'A'; i < 'A' + l; i++) {
			if(isHard(prefix, i)) { //是困难的串就组合起来输出
				String x = prefix + i;
				System.out.println(x);
				count++;
				if(count == n)
					System.exit(0);
				
				dfs(l,n,x);
			}
		}
		
	}

	/*
	 * 判断prefix是否是一个困难的串
	 * 1、遍历所有长度为偶数的子串,看是否对称
	 * 2、prefix是一个困难的串
	 */
	public static boolean isHard(String prefix, char i) {
		int count = 0;//截取的宽度
		for(int j = prefix.length()-1; j >= 0; j -= 2) {
			final String s1 = prefix.substring(j, j + count +1);
			final String s2 = prefix.substring(j + count + 1) + i;
			if(s1.equals(s2))
				return false;
			count++;
		}
		return true;
	}
}

数独游戏

/*
 * 如下图所示,玩家需要根据9*9盘面上的一直数字,推理出所剩余空格的数字,并满足每一行、每一列、每一个同色九宫格内的数字均含1-9不重复
 * 数独的答案是唯一的,所以,多个解也称为无解
 * 输入:
 * 005300000
 * 800000020
 * 070010500
 * 400005300
 * 010070006
 * 003200080
 * 060500009
 * 004000030
 * 000009700
 * 输出:
 * 145327698
 * 839654127
 * 672918543
 * 496185372
 * 218473956
 * 753296481
 * 984761235
 * 521839764
 */
public class _数独游戏 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		char[][] table = new char[9][];
		for(int i = 0;i<9;i++) {
			table[i] = sc.nextLine().toCharArray();
		}
		dfs(table,0,0);
	}

	public static void dfs(char[][] table, int x, int y) {
		if(x == 9) {
			print(table);
			System.exit(0);
		}
		if(table[x][y] == '0') {
			for(int k = 1;k<10;k++) {
				if(check(table,x,y,k)) {
					table[x][y] = (char)('0'+k);
					dfs(table,x+(y+1)/9,(y+1)%9); //处理下一个状态
				}
			}
			table[x][y] = '0';//回溯
		}else {
			dfs(table,x+(y+1)/9,(y+1)%9);//处理下一个状态
		}
	}

	public static boolean check(char[][] table, int x, int y, int k) {
		//检查同行和同列
		for(int l = 0;l < 9;l++) {
			if(table[x][l] == (char)('0'+k))
				return false;
			if(table[l][y] == (char)('0'+k))
				return false;
		}
		//检查小宫格
		for(int l = (x/3)*3;l<(x/3+1)*3;l++) {
			for(int m = (y/3)*3;m<(y/3+1)*3;m++) {
				if(table[l][m] == (char)('0'+k))
					return false;
			}
		}
		return true;
	}

	public static void print(char[][] table) {
		for(int i = 0;i<table.length;i++) {
			System.out.println(new String(table[i]));  
		}
	}

}

素数环

/*
 * 输入正整数n,对1-n进行排序,使得相邻两个数之和均为素数
 * 输出时从整数1开始,逆时针排序,同一个环恰好输出一次
 * 输入:6
 * 输出:
 * 1 4 3 2 5 6
 * 1 6 5 2 3 4
 */
public class _素数环 {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] r = new int[n];
		r[0] = 1;
		dfs(n,r,1);
	}

	public static void dfs(int n, int[] r, int cur) {
		if(cur == n && isP(r[0] + r[n-1])) {//填到末尾,首尾相加为素数才算成功
			print(r);
			return;
		}
		for(int i = 2; i <= n; i++) {//尝试用每个数字填到cur这个位置
			if(check(r,i,cur)) { //r中没有这个数,且和上一个数之和为素数
				r[cur] = i;;//试着将i放在cur位置,往前走一步
				dfs(n,r,cur+1);
				r[cur] = 0;//回溯
			}
		}
	}

	public static boolean check(int[] r, int i, int cur) {
		for(int e : r) {
			if(e == i || !isP(r[cur-1] + i))
				return false;
		}
		return true;
	}

	public static boolean isP(int k) {//判断k是否是素数
		for(int i = 2; i*i <= k; i++) {
			if(k % i == 0)
				return false;
		}
		return true;
	}

	public static void print(int[] r) {
		for(int i = 0; i < r.length; i++) {
			System.out.print(r[i] + (i==r.length-1?"":" "));
		}
		System.out.println();
	}

}

硬币表示

/*
 * 假设我们有8种不同面值的硬币{1,2,5,10,20,50,100,200},用这些硬币组合构成一个给定的数值n
 * 共有多少种组合方式
 * 【华为面试题】1分2分5分的硬币有三种,组合成1角,共有多少种组合
 * 【创新工厂笔试】有1分2分5分10分四种硬币,每种硬币数量无限,给定n分钱,有多少种组合可以组成n分钱
 */
public class _硬币表示 {

	public static void main(String[] args) {
		/*
		 * for(int i = 1;i < 101;i++) { int ways = countWays(i); System.out.println(i +
		 * "-->" + ways); }
		 */
		//假设有1元5元10元25元,要凑成100元
		System.out.println(countWays(100));
	}

	public static int countWays(int n) {
		if(n <= 0)
			return 0;
		return countWaysCore(n,new int[]{1,5,10,25},3);
	}

	public static int countWaysCore(int n, int[] coins, int cur) {
		if(cur == 0)
			return 1;
		int res = 0;
		for(int i = 0; i*coins[cur] <= n;i++) {
			int left = n - i*coins[cur];
			res += countWaysCore(left,coins,cur-1);
		}
		return res;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值