代码貌似不对,再放放… …
Time Limit: 1000MS Memory Limit: 10000K
Description
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
Input
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。
Output
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
Sample Input
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
Sample Output
6
5
Source
Central Europe 1995
解释:
看下面程序中的注释即可,
特别的,如果a[i]==rest,a[i]已经失败了,所以和它相同的rest长度肯定也会失败。
如果rest==len说明,说明在用a[i]之前没有用过任何木棍,那么以a[i]为第一根的情况失败了,那么这根木棍肯定拼不成目标长度,这个搜索也就没有意义了,直接跳出大循环。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int a[55];
bool vis[55];
int n;
bool cmp(const int a,const int b)
{
if(a>=b)
return 1;
else
return 0;
}
/*DFS:递归搜索,以剩下小棒数和拼接一根棒所需长度作为递归参数*/
bool dfs(int len,int num,int rest)//..,剩余的小棒数目
{
if(num==n && rest==0)/*当还需长度为0且未作接接的小棒个数为0时,说明拼接成功*/
return len;
return 1;
if(rest==0)/*当完成拼接一个棒时,要重新进行下一根木棒拼接,给remains_len重新赋值*/
rest=len;
for(int i=0;i<n;i++)
{
if(vis[i]) /*已经作为成功拼接的小棒,不再作为拼接对象*/
continue;
if(rest>=a[i])
{
vis[i]=1;
if(dfs(len,num-1,rest-a[i]))/*当接受这根小棒a[i],能完成所有任务,则返回成功拼接棒长*/
return 1;
vis[i]=0;
if(a[i]==rest && len==rest)//这一步很重要,看上面的分析
break;
while(a[i+1]==a[i])/*当a[i]不能完成任务,与它相同的小棒也不能完成任务(剪枝) */
i++;
}
}
return 0;
}
int main()
{
while(scanf("%d",&n) && n)
{
int sum=0;
int len;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];//a[1] a[2] a[3] a[4]
}
sort(a,a+n,cmp);
for(len=a[0];len<=sum;len++)/*每次尝试都要置所有小棒为可用*/
{
memset(vis,0,sizeof vis);
if(sum%len==0)
{
if(dfs(len,n,0))//要拼成len的长度,用过的木棍数数量,剩余长度
{
cout<<len<<endl;
break;
}
}
}
}
return 0;
}
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=70;
int n,a[N],cnt,maxn,goal;
bool cmp(int x,int y){return x>y;}
bool use[N];
inline void dfs(int ans,int num,int pre){//当前拼成的长度,目标大木棍的数目,当前搜索小木棍的位置
if(num==0){printf("%d\n",goal); exit(0);}
if(maxn-ans<a[cnt])return; //"2"如果剩余长度小于最小长度return
if(ans==goal)
{
dfs(0,num-1,1); //如果失败return
return;
}
for(register int i=pre;i<=cnt;i++)//"1"
if(!use[i] && ans+a[i]<=goal){
use[i]=1;
dfs(ans+a[i],num,i+1);
use[i]=0;
while(a[i]==a[i+1])i++;//"3"17,9,9,9,9,8,5,2……如果当前最长小棒为17,它与第一个9组合之后dfs发现不能拼成len
if(ans+a[i]==goal || ans==0) return; //"4"...8 7 5 3 3 2 2->15
}//如果8+7成功,但后面的失败了,就不用再搜索8+5等等了。
//反证法:因为如果8+后面的A成功了,且7+后面的B也成功了,那么8+7和A+B也都成功了,和结论相悖。
//a[i]为第一根的情况失败了,那么这根木棍肯定拼不成目标长度,
return ;
}
int main(){
scanf("%d",&n);
for(register int i=1;i<=n;i++){
int x;
scanf("%d",&x);
if(x<=50){a[++cnt]=x; maxn+=a[cnt];}
}
sort(a+1,a+cnt+1,cmp);
goal=a[1];
while(goal<=maxn/2)//"0"
{
if(maxn%goal==0)
dfs(0,maxn/goal,1);//当前拼成的长度,目标大木棍数目,上一个数的位置
goal++;
}
printf("%d\n",maxn);
return 0;
}