P1423【分类练习6.搜 索】小木棍 | |
|
问题描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入格式
输入文件共有二行。
第一行为一个单独的整数N表示看过以后的小木柜的总数,其中N≤60,
第二行为N个用空个隔开的正整数,表示N跟小木棍的长度。
输出格式
输出文件仅一行,表示要求的原始木棍的最小可能长度。
样例输入
9
5 2 1 5 2 1 5 2 1
样例输出: 6
分析:
首先答案肯定是所有木棍长度之和的因数,并且比最长的木棍长。
从小到大枚举每一个这样的数,用DFS来验证。
DFS思路:凑成一个一个长木条,凑足数量为止。
DFS(x, used, cur) 表示当前试用x号木棍,当前的这根大木条已经用了used米,凑好的大木条的总长。
剪枝:
(1)每凑完一根大木条,新的大木条用上剩下的木棍中最长的那一根,这样对后面约束能力更强。
(2)(强) 如果有一样的木棍,只讨论一次。
貌似这两条已经能a了。
为了方便处理,将所有木棍从大到小排序。
代码如下:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=70,inf=1e9;
int n,len,s[maxn],tot,_min,_max;
vector<int> divs;
bool vis[maxn];
void depart(int p){ //找出要枚举的因数
divs.clear();
int m=(int)sqrt(p);
for(int i=1;i<=m;i++)
if(p%i==0){
if(i>=_max)divs.push_back(i);
if(i*i!=p&&p/i>=_max) divs.push_back(p/i);
}
sort(divs.begin(),divs.end());
}
bool dfs(int x,int used,int cur){
int i;
if(len==used){
cur+=len;
if(cur==tot) return true;
x=1;
while(vis[x])x++; //因为是排过序的,找出剩的最长的木棍
vis[x]=true;
if(dfs(x,s[x],cur)) return true;
vis[x]=false;
}
else {
for(i=x;i<=n;i++){
if(i>1&&s[i]==s[i-1]&&!vis[i-1]) continue; //避免重复搜索相同长度
if(!vis[i]&&used+s[i]<=len){
vis[i]=true;
if(dfs(i,used+s[i],cur)) return true;
vis[i]=false;
}
}
}
return false;
}
bool cmp(int x,int y){
return x>y;
}
int main(){
int i,j;
while(cin>>n&&n){
tot=_max=0; _min=inf;
for(i=1;i<=n;i++){
cin>>s[i];
tot+=s[i];
_max=max(_max,s[i]);
}
sort(s+1,s+1+n,cmp);
depart(tot);
for(i=0;i<divs.size();i++){
CLEAR(vis);
len=divs[i];
if(dfs(1,0,0)){
cout<<divs[i]<<endl;
break;
}
}
}
return 0;
}
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=70,inf=1e9;
int n,len,s[maxn],tot,_min,_max;
vector<int> divs;
bool vis[maxn];
void depart(int p){ //找出要枚举的因数
divs.clear();
int m=(int)sqrt(p);
for(int i=1;i<=m;i++)
if(p%i==0){
if(i>=_max)divs.push_back(i);
if(i*i!=p&&p/i>=_max) divs.push_back(p/i);
}
sort(divs.begin(),divs.end());
}
bool dfs(int x,int used,int cur){
int i;
if(len==used){
cur+=len;
if(cur==tot) return true;
x=1;
while(vis[x])x++; //因为是排过序的,找出剩的最长的木棍
vis[x]=true;
if(dfs(x,s[x],cur)) return true;
vis[x]=false;
}
else {
for(i=x;i<=n;i++){
if(i>1&&s[i]==s[i-1]&&!vis[i-1]) continue; //避免重复搜索相同长度
if(!vis[i]&&used+s[i]<=len){
vis[i]=true;
if(dfs(i,used+s[i],cur)) return true;
vis[i]=false;
}
}
}
return false;
}
bool cmp(int x,int y){
return x>y;
}
int main(){
int i,j;
while(cin>>n&&n){
tot=_max=0; _min=inf;
for(i=1;i<=n;i++){
cin>>s[i];
tot+=s[i];
_max=max(_max,s[i]);
}
sort(s+1,s+1+n,cmp);
depart(tot);
for(i=0;i<divs.size();i++){
CLEAR(vis);
len=divs[i];
if(dfs(1,0,0)){
cout<<divs[i]<<endl;
break;
}
}
}
return 0;
}