Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 145712 | Accepted: 34485 |
Description
Input
Output
Sample Input
9 5 2 1 5 2 1 5 2 1 4 1 2 3 4 0再测这组数据看看915 11 8 8 8 4 3 2 1 结果:20
Sample Output
6 5
翻译:这也是学英语的一部分
乔治拿了等长度的木条,直至把这些木条分成不超过50的部分,现在他想还原这些木条,但他忘记了原来是那个和原来多长,请帮他找到木条可能的最小值
输入:包括两行,第一行:分成多少条小木条(最多64条)
第二行:每条木条的长度
输出:一行,最小的可能的原木条长度
最初思路:
我们可以知道所有的原木条长度是一样的,而且>=5.我们知道分成小木条之后,这些小木条之和肯定是原木条长度的倍数,长度不会多出来。以
9 5 2 1 5 2 1 5 2 1为例。sum=24 那么 只要24/X是整数且X越小越好 且X>=小木条最大的数,求出X就好了。
然而只要考虑这些就够了吗,结果是否定的。我一开始认为只要求满足的最小约数就好了,但是想一下,你算出来的最小约数真的能被这些小木条凑起来吗,万一你的约数是7,但你的小木条都凑不出7,那不就开玩笑了吗。所以怎么办?但是我们可以知道原木条的长度肯定是>=小木条最大值
&& 原木条的长度是sum的约数
解决问题:
那我们肯定是要找匹配这些约数,看哪个最小的约数可以满足让这些小木条正好可以凑起来。我们肯定是要一个一个的找喽---索搜
我们最好从大到小的找(所以我们从大到小排序),这样可以减少运行量,那我们怎么样才算找到呢?我们先拿出第一个数,发现还差一点点,找第二个数,直至找到相等,如果没有相等,则这个约数不是要找的数。如果这k个数加起来是这个数,我们还要往下看,剩下的数还有没有加到这个约数。直至结束。那么结束条件是什么呢?我们每加上一个数,那么就设这个数列的长度+1,如果这个数列长度==原数列,那么我们说,确实是这个数。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int vis[65];
int t[65];
bool flag;
bool cmp(int a,int b){
return a>b;
}
//cur_len为当前的长度(如果有一组数==len[约数]会置0)
//strlen为数列的长度
//maxlen是小木条的数量(最后是数列长度==小木条数量-->成功)
//len是约数
void dfs(int cur_len,int strlen,int index,int len,int maxlen){
if(flag) return;
if(cur_len==len)
{
if(strlen==maxlen)
{
flag=true;
}
else
{
dfs(0,strlen,0,len,maxlen);
}
return;
}
if(cur_len==0){
int k=0;
while(vis[k]) k++;
vis[k]=1;
dfs(t[k],strlen+1,k+1,len,maxlen);
vis[k]=0;
return;
}
for(int i=index;i<maxlen;i++)
{
if(vis[i]==0)
{
if(cur_len+t[i]<=len)
{
if(!vis[i-1] && t[i] == t[i-1]) continue;
vis[i]=1;
dfs(cur_len+t[i],strlen+1,index+1,len,maxlen);
vis[i]=0;
}
}
}
}
int main(){
int T=0;//小木条
while(cin>>T&&T!=0){
int sum=0;
flag=false;
for(int i=0;i<T;i++) //输入数据
{
cin>>t[i];
sum+=t[i];
}
sort(t,t+T,cmp); //排序
int i;
for(i=t[0];i<sum;i++)
{
if(sum%i==0) //从小到大遍历每个约数
{
memset(vis,0,sizeof(vis));
dfs(0,0,0,i,T); //判断是否可以凑出这个长度
if(flag) break;
}
}
cout<<i<<endl;
}
return 0;
}