If not me,who?
错误的思路,
我用了n个小时验证我的思路是有问题的。。。将小木棍从大到小排序后。然后开始从大的木棍开始选取,在选取下一个木棍的时候也是尽量从大的开始选取,但是问题就出现了,这样选取的结果可能不是最优的。。。(就是说会造成不恰当的选取),怎莫说呢,你第一个选取了第i个小木棍,作为初始的小木棍,然后又挑选了一个尽量大的小木棍,作为下一个选取的对象,这个时候两个大的小木棍叠加之后,可能还需要一个很小的木棍就能拼成了。但是我们的原则是尽量留小的木棍,可能本来我们用两个中等长度的小木棍就可以搞定,如此选取就可能会出错。
然后就看了人家的代码,自己看懂了思路之后开始自己写,然后发现tle,就照着大佬的代码改,不知不觉就变成了人家代码的样子,这难道是菜鸟的必经历程?
思路
深搜首先是要找最终结束的标志,这个题目最终结束的标志就是找够sum/len条拼接成功的木棍。深搜的时候,还有一个问题就是当前状态能不能拼接成功,也取决于前面的选择方案。如果前面的选择方案是有问题的,在当前不能拼接成功的情况下,要保证此时return fasle之后,前面的拼接会发生改变。所以有了我下面标注星号的一行代码,前面拼接的情况能够直接的返回true的条件就是后面的情况是可以拼接成功的。(当时我就不知道怎样实现这一点)
for(int i=pre;i<=N;i++){
if(!vis[i]&&sum+m[i]<=len&&flag!=m[i]){
vis[i]=1;
******** if(dfs(stick,sum+m[i],i+1)) return true;
flag=m[i];
vis[i]=0;
if(sum == 0 || sum+m[i] == len) return false;
}
}
另外剪枝,真的很重要,还很不好想,特别是下面的第三个剪枝。
剪枝
- 排序剪枝
- 重复
当前小木棍不满足条件,那么和它长度相同的小木棍都不满足条件。 - 如果当前这个小木棍直接不能拼成,那么就直接return false。这个小木棍如果一个在当前的情况下一个拼接的方案也没有的话,就说明之前的方案是有问题的。所以直接返回
***********************不加这个直接tle
if(sum == 0 || sum+m[i] == len) return false;
#include <bits/stdc++.h>
using namespace std;
const int maxn=100;
int m[maxn];
int vis[maxn];
int N,cnt;//cnt记录的是当前的长度可以被分成几部分
int len;
bool dfs(int stick,int sum,int pre)//从第i个结点开始
{
if(stick==cnt+1) return true;
if(len==sum) return dfs(stick+1,0,1);
int flag=-1;//加上一个剪枝
for(int i=pre;i<=N;i++){
if(!vis[i]&&sum+m[i]<=len&&flag!=m[i]){
vis[i]=1;
if(dfs(stick,sum+m[i],i+1)) return true;
flag=m[i];
vis[i]=0;
if(sum == 0 || sum+m[i] == len) return false;
}
}
return false;
}
int main()
{
int temp,sum=0,i,j,k;
cin >> N;
for(i=1;i<=N;i++){
scanf("%d",&temp);
if(temp>50)
N--,i--;
else{
m[i]=temp;
sum+=temp;
}
}
if(0==N){
printf("0\n");
return 0;
}
sort(m+1,m+1+N,greater<int>());
for(len=m[1];len<sum;len++){
if(0==sum%len){
cnt=sum/len;
memset(vis,0,sizeof(vis));
if(dfs(1,0,1)) break;
}
}
printf("%d",len);
return 0;
}