小木棍[数据加强版]题解
小木棍 [数据加强版]
提高+/省选-
题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入格式
共二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65
(管理员注:要把超过5050的长度自觉过滤掉,坑了很多人了!)
第二行为N个用空个隔开的正整数,表示N根小木棍的长度。
输出格式
一个数,表示要求的原始木棍的最小可能长度
输入输出样例
输入 #19 5 2 1 5 2 1 5 2 1输出 #16说明/提示
2017/08/05
数据时限修改:
-#17 #20 #22 #27 四组数据时限500ms500ms
-#21 #24 #28 #29 #30五组数据时限1000ms1000ms
其他时限改为300ms300ms(请放心食用)
暴搜肯定过不了,是要优化的
思路就是枚举小木棍的长度,从小到大枚举
为了方便,我们将没砍的叫长木棍,砍了的叫小木棍
- 以最长的小木棍的长度枚举,很容易理解吧,不可能砍了再变长
- 总长度必须是长木棍长度的倍数,这就不用解释了吧
- 一旦有一个分枝搜索失败,即有一根小木棍无论与其他的如何拼,也出现不了我们需要的长度,直接返回
- 相同的木棍不需要重复搜索,其中一个失败,其它肯定失败。
- 降序排序,sort一下
#include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int used[100],a[100],num,n,len,sum; bool cmp(int x,int y) { return x>y; } bool dfs(int stick,int cab,int last) { if(stick>num) return true; //选够了num个了,并且所有小木棍都用完了,返回true, if(cab==len) return dfs(stick+1,0,1); //拼成一个长木棍,再拼下一个长木棍 int ajh_ak_IOI=0; for(int i=last;i<=n;i++) { if(!used[i]&&a[i]+cab<=len&&(ajh_ak_IOI!=a[i])) //ajh_ak_IOI!=a[i],即剪枝4 { used[i]=1; if(dfs(stick,cab+a[i],i+1)) return true; used[i]=0; ajh_ak_IOI=a[i]; if(cab==0||cab+a[i]==len) return false ;//剪枝 3 } } return false; } int main() { while(cin>>n&&n) { int val=0; int Q,m=0; sum=0; for(int i=1;i<=n;i++) { scanf("%d",&Q); if(Q<=50) //大于50的自动过滤掉,详见题目描述 { a[++m]=Q; sum+=a[m]; val=max(val,a[m]); } } n=m; sort(a+1,a+1+n,cmp); //剪枝5 for(int i=val;i<=sum;i++) //剪枝1 { if(sum%i!=0) continue; //剪枝2 memset(used,0,sizeof(used)); len=i; num=sum/i; if(dfs(1,0,1)) break; } cout<<len<<endl; } }
调了3个小时代码不容易啊,支持一下再走吧