POJ 1011 Sticks DFS 剪枝

Sticks
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 113953 Accepted: 26179

Description

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

Input

The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output

The output should contains the smallest possible length of original sticks, one per line.

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

Source


POJ 2362的加强版,剪枝策略我没想到。参考了别人的题解⊙﹏⊙b 还有一个很坑的事情。我 使用全局变量作为递归函数的循环变量,WA了一晚上。


搬运下剪枝策略:http://blog.163.com/xdu_cfcry/blog/static/1694623032010718274132/

-----------------------------------------------------搬运部分-----------------------------------------------------

经典搜索题,黑书上的剪枝例题。剪枝的空间很大,剪枝前写下朴素的搜索框架(黑字部分),枚举原始木棍的长度及由那些小木棍组合。原始木棍长度一定是小木棍总长度的约数,因此可以减少枚举量。

          越长的木棍对后面木棍的约束力越大,因此要把小木棍排序,按木棍长度从大到小搜索,这样就能在尽可能靠近根的地方剪枝。(剪枝一)

          如果当前木棍能恰好填满一根原始木棍,但因剩余的木棍无法组合出合法解而返回,那么让我们考虑接下来的两种策略,一是用更长的木棍来代替当前木棍,显然这样总长度会超过原始木棍的长度,违法。二是用更短的木棍组合来代替这根木棍,他们的总长恰好是当前木棍的长度,但是由于这些替代木棍在后面的搜索中无法得到合法解,当前木棍也不可能替代这些木棍组合出合法解。因为当前木棍的做的事这些替代木棍也能做到。所以,当出现加上某根木棍恰好能填满一根原始木棍,但由在后面的搜索中失败了,就不必考虑其他木棍了,直接退出当前的枚举。(剪枝二)

         显然最后一根木棍是不必搜索的,因为原始木棍长度是总木棍长度的约数。(算不上剪枝)

         考虑每根原始木棍的第一根木棍,如果当前枚举的木棍长度无法得出合法解,就不必考虑下一根木棍了,当前木棍一定是作为某根原始木棍的第一根木棍的,现在不行,以后也不可能得出合法解。也就是说每根原始木棍的第一根小木棍一定要成功,否则就返回。(剪枝四)

         剩下一个通用的剪枝就是跳过重复长度的木棍,当前木棍跟它后面木棍的无法得出合法解,后面跟它一样长度的木棍也不可能得到合法解,因为后面相同长度木棍能做到的,前面这根木棍也能做到。(剪枝五)

         这样剪枝基本就结束了,我们发现,每种剪枝都只是加了一条语句,但剪枝效果非常明显。uva307题目跟这个一模一样,当时uva的数据规模更强,许多在poj 10+MS的程序在uva 3000MS都跑不出来,当时我的加上这些剪枝,我的程序在uva也能跑出靠前的成绩。  rank:38      time:0.244s

         剪枝要平衡准确性和额外花费的关系,一开始我用上下界剪枝,这个额外花费相当大,每次搜索一根原始木棍都要更新从某根木棍开始到最后一根可用木棍的总长度,对于一般的数据确实能跑的比没加剪枝快,但对苛刻的,第一根木棍总不成功的数据,这个剪枝就成了程序的瓶颈,导致我在poj都超时了,删除后0MS, 汗~~。

-----------------------------------------------------搬运部分-----------------------------------------------------

#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int a[70],b[70],n,i,sum,minn;
int cmp(int a,int b){return a>b;}
bool dfs(int o,int p,int q);
int main()
{
	while (cin>>n && n){
		memset(b,0,sizeof(b)); sum=0;
		for (i=0;i<n;++i){
			cin>>a[i];
			sum+=a[i];
		}
		sort(a,a+n,cmp);
		for (minn=a[0];minn<sum;++minn)
			if (sum%minn==0 && dfs(0,minn,sum)) break;
		cout<<minn<<endl;
	}
	return 0;
}

bool dfs(int o,int p,int q){
	if (q==minn) return true;
	for (int i=o;i<n;++i)
		if (!b[i] && a[i]<=p){
			b[i]=true;
			if (a[i]==p)
				{if (dfs(0,minn,q-a[i])) return true;} 
			else
				if (dfs(i+1,p-a[i],q-a[i])) return true;
			b[i]=false;
			if (a[i]==p) return false;	//剪枝2 
			if (q==sum) return false;	//剪枝3 
			if (p==minn) return false;	//剪枝4 
			while (a[i+1]==a[i] && i<n) i++;  //剪枝5 
		}
	return false;
}

kdwycz的网站:  http://kdwycz.com/

kdwyz的刷题空间:http://blog.csdn.net/kdwycz






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值