https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=243
题意:有n根木棍,要求将所有木条拼接成等长的若干木条,求原始拼接长度的最小值
分析:dfs+剪枝题目说拼接总长度不大于50,数组长度要开到100以上吧(因为这个问题,t了我好久)
强力剪枝6连:
1.枚举原始木棒长度从max-sum/2,如果在此部分还没有找到答案,则sum为答案
2.长度从大到小排好序,如果枚举到当前的len,sum%len||a[0]>len那么这个长度肯定是不行的,用while,直接i++
3.连续相同的数组中元素,如果:i有前导,且a[i]=a[i-1],且a[i-1]还没有被用掉,说明这个数是不行的,如6,4,4,1,可以想象
4.如果当时前正在组的木棒长度为0,且当前遍历到的最大的可用a[i]不能达成成就,说明这个a[i]在组成的过程中一定不会用到,与题目矛盾,也即这种情况不可能,剪枝,return 0
5.如果当前的长度加上a[i]组成一个len但是还是不能达成总成就(有若干len长的原始木棒),说明前面都是的,剪枝,return
代码:
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e5 + 10;
int a[maxn];
int vis[maxn];
int sum=0;
int n;int cmp(int a,int b){return a>b;}int dfs(int len,int cur,int other,int cnt){if(cnt==sum/len)return 1;for(int i=other;i<n;i++){while(i&&a[i]==a[i-1]&&!vis[i-1])i++;if(cur+a[i]<=len&&!vis[i]){if(cur+a[i]<len){vis[i]=1;if(dfs(len,cur+a[i],i+1,cnt)){return 1;}vis[i]=0;if(cur==0){return 0;}}if(cur+a[i]==len){vis[i]=1;if(dfs(len,0,0,cnt+1)){return 1;}vis[i]=0;return 0;}}}return 0;
}int main(){while(~scanf("%d",&n),n){memset(vis,0,sizeof(vis));int m=0;sum=0;for(int i=0;i<n;i++){scanf("%d",&a[i]);sum+=a[i];m=max(m,a[i]);}sort(a,a+n,cmp);int flag=0;for(int l=m;l<=sum/2;l++){while(a[0]>l||sum%l)l++;if(dfs(l,0,0,0)){printf("%d\n",l);flag=1;break;}}if(!flag)printf("%d\n",sum);}
}