大致思路:
我傻了,我竟然在纠结“咦我咋知道它们应该要拼的长度(即递归临界条件)是啥呀?”你竟然还在纠结??不就是遍历解决的事情吗。。。。。。有时候还是要往暴力去想。
然后就是常规的dfs,真的思想很常规,就是访问了之后在当前基础去递归,不行的话就再回来当作什么也没发生过继续遍历进入下一个选择。主要难在一些剪枝(避免超时呀)的设计上。
觉得这道题剪枝的设计比较有趣的地方在于,由于“每个木棒都必须要使用到”(这个须读题注意哟!),如果选择这个木棒去递归是失败的,而①“这个木棒长度==假设的原始木棍长”,那么你之后的木棒也没用啊是不是 ; ②如果“你此时的剩余长度为L(即新开了一个原始木棍的拼凑计划),你用上了当前木棒却失败了”,那么你这个最小木棍长的假设也是错的。 都是基于“每个木棒都必须要使用到”而设计出的剪枝,我只想说牛b!
AC代码(copy的):
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define MAXN 100005
#define LL long long
#define INF 2147483640
#define MOD 100000007
#define free(s) freopen("s.txt","r",stdin);
using namespace std;
bool cmp(int x,int y)
{
return x>y;
}
int n,t,sum=0,cnt=0,a[70],res,vis[70],pp;
int dfs(int len,int sta,int now)// dfs(剩下的长度,第几个木棍开始,已经摆好了几组)
{
if(now==res)
return 1;
if(len==0)
if(dfs(pp,1,now+1))// 当前这一根已经摆完 剩下的长度应该重新开始
return 1;
for(int j=sta;j<=cnt;j++)
if(!vis[j]&&a[j]<=len)
{
vis[j]=1; // 保证点未被用过
if(dfs(len-a[j],j+1,now)) // 搜下一个点
return 1;
vis[j]=0;
if(len==a[j]||len==pp)// 首先:能运行到这表明最大的a[i]也不能满足条件 然后:如果len==pp 即一组新的木棍 那么以后的也不能满足条件
break; // 用了当前木棍后无法拼好 但是剩下的木棍长度还等于当前木棍长度 跳出
while(a[j+1]==a[j]) // 当前长度不行 其他长度也不行
j++;
}
return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
if(t<=50)
{
sum+=t;
a[++cnt]=t;
}
}
sort(a+1,a+1+cnt,cmp);
for(int i=a[1];i<=sum;i++)
if(sum%i==0)
{
pp=i; // 全局记录长度
res=sum/i;
if(dfs(i,1,0))
{
printf("%d",i);
return 0;
}
}
return 0;
}