POJ 1011 Sticks

传说中的入门级经典DFS搜索+剪枝,果然一看答案就会了微笑。。。

因为不存在顺序问题,当不符合条件时长棍更容易检测出来,所以从大到小排列。

朴素的DFS搜索:

int dfs( int temp_sum, int remain, int num){
	int i;
	if( remain == 0){
		if( num == 0) return 1;
		else remain = temp_sum;
	}
	for( i = 0; i < n; i++){
		if( vis[i] == 0){
			if( remain >= a[i]){
				vis[i] = 1;
				if( dfs( temp_sum, remain-a[i], num-1)) return 1;
				vis[i] = 0;
			}	
		}
	}
	return 0;
}

剪枝思路:

① 原最小棍长一定是总和的因数

② 如果棍长不符合当前所有棍的和,就放弃搜索

③ 相等棍长的棍只用搜索一次


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp( const void* a, const void* b){                          // 从大到小排列
	return *(int*)b - *(int*)a;
}
int ans, n;
int a[100], vis[100];
	
int dfs( int temp_sum, int remain, int num);
int main(void){
	int i, sum;

	while( scanf("%d", &n)!= EOF && n){
		sum = 0;
		for( i = 0; i < n; i++){
			 scanf("%d", &a[i]);
			 sum += a[i];                           // 在这里求所有棍长的和
		}
		qsort( a, n, sizeof(int), cmp);
		memset( vis, 0, sizeof(vis));                   // 把访问数组设置为0
		for( ans = 2; ans < sum; ans++){
			if( sum % ans) continue;                // 剪枝1:原最小棍长一定是总和的因数
			else if( dfs( ans, ans, n)) break;
		}
		printf("%d\n", ans);
	}
	return 0;
}
int dfs( int temp_sum, int remain, int num){                    // 参数设置:暂时的棍长用来试验,余下的长度,余下的棍数
	int i;
	if( remain == 0){                                       // 满足了一次棍长
		if( num == 0) return 1;                         // 若此时所有的木棍都被用过,则此时的棍长满足题意,返回成功
		else remain = temp_sum;                         // 否则重置剩余棍长,继续试验
	}
	for( i = 0; i < n; i++){                                // 从第0根木棍开始试验
		if( vis[i] == 0){                               // 只试验未被访问的棍
			if( remain >= a[i]){                    // 若此时棍长小于剩余棍长
				vis[i] = 1;                     // 假设使用
				if( dfs( temp_sum, remain-a[i], num-1)) return 1;   // 继续向下搜索,若满足条件停止搜索
				vis[i] = 0;                                         // 所有搜索都不满足条件,则此木棍不在当前组合中
				if( a[i] == remain || remain == temp_sum) break;    // 剪枝2:若没有符合条件的木棍,说明当前棍长不符合条件,认为失败,停止搜索
				while( a[i] == a[i+1]) i++;                         // 剪枝3:如果随后的木棍和此木棍相同长度,则跳过不搜索
			}
		}
	}
	return 0;                                               // 返回失败
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值