[Java] POJ 1011 经典搜索剪枝题

一、思路

  • 首先想到的是 枚举下一根棒的长度进行dfs,但是题目给的是64根棒,直接dfs肯定会超时,所以要进行剪枝。
  • 进行剪枝:
    1.一根棒的长度肯定是大于等于所有小棒的最大值的,所以我们从最大值开始枚举,枚举到最大值/2。
    2.将保存小棒的长度的数组从大到小排序,每次从大的开始选择。因为要组成一根棒,必然要先用到大的小棒。
    3.将所有小棒的总长度和保存在sum, 如果sum不能被棒的长度整除,那么直接跳过这种可能。
    	//从大到小进行排序
    		for (int i = 0; i < n; i++) {
    			for (int j = 0; j < n - i - 1; j++) {
    				if (rec[j] < rec[j + 1]) {
    					int tem = rec[j];
    					rec[j] = rec[j + 1];
    					rec[j + 1] = tem;
    				}
    			}
    		}
    		//枚举下棒的长度
    		ok = false;
    		for (int i = rec[0]; i <= sum / 2; i++) {
    			if (sum % i != 0) continue;  //总和 不能被棍的长度整除 不满足 直接continue
    			L = i; //棒的长度
    			dfs(0, 0, 0);
    			if (ok) {
    				System.out.println(i);
    				break;
    			}
    		}
    
    4.最重要的剪枝:如果当前选择的这个棒与上一根棒的长度相同,但是上一根棒没有被选,那么这根小棒也不用选上去了。
    //剪枝   必须加上这个  不然超时 因为上一根棒如果和当前棒是一样长,但是上一个棒没有使用,那么这根棒也不需要使用
    				if (!vis[i - 1] && rec[i- 1] == rec[i]) continue; 
    

二、代码

import java.util.*;


public class POJ_1011 {
	static int n, sum, L;
	static int[] rec = new int[100];
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while ((n = sc.nextInt()) != 0) {
			sum = 0;
			for (int i = 0; i < n; i++) {
				rec[i] = sc.nextInt();
				sum += rec[i];
			}
			//从大到小进行排序
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < n - i - 1; j++) {
					if (rec[j] < rec[j + 1]) {
						int tem = rec[j];
						rec[j] = rec[j + 1];
						rec[j + 1] = tem;
					}
				}
			}
			//枚举下棒的长度
			ok = false;
			for (int i = rec[0]; i <= sum / 2; i++) {
				if (sum % i != 0) continue;  //总和 不能被棍的长度整除 不满足 直接continue
				L = i; //棒的长度
				dfs(0, 0, 0);
				if (ok) {
					System.out.println(i);
					break;
				}
			}
			//上面没有找到结果
			if (!ok)System.out.println(sum);
		}
	}
	static boolean[] vis = new boolean[100];//记录该棒是否被访问过
	static boolean ok;
	// cnt是使用了的棒的数量  num是棒的长度和 index是开始搜索的下标,每次从前一根棒的后面进行搜
	static void dfs(int cnt, int num, int index) { 
		if (ok) return; // 找到了结果
		if (cnt == n) { //n个棒都使用完了  代表找到了结果
			ok = true;
			return;
		}
		if (num == 0) { //选一根作为开头。  因为数组是从大到小的排序。  所以开头的必然在前面
			//选择一根作为头开始
			int k = 0;
			while (vis[k]) k++;
			vis[k] = true;
			dfs(cnt + 1, rec[k], k + 1);
			vis[k] = false;
			return;
		}
		
		if (num == L) { 
			//已经满足一根棒  进行下一根棒的搜索
			dfs(cnt, 0, 0);
			return;
		}
		
		for (int i = index; i < n; i++) {
			if (!vis[i] && num + rec[i] <= L) {
				//剪枝   必须加上这个  不然超时 因为上一根棒如果和当前棒是一样长,但是上一个棒没有使用,那么这根棒也不需要使用
				if (!vis[i - 1] && rec[i- 1] == rec[i]) continue; 
				vis[i] = true;
				dfs(cnt + 1, num + rec[i], i + 1);
				vis[i] = false;
			}
		}
	}
	
	static int max(int a, int b) {
		return a > b ? a : b;
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值