洛谷P1120 小木棍
洛谷P1120 小木棍
题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50
。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:
共二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中 $N \leq 65 $
第二行为 \(N\) 个用空个隔开的正整数,表示 \(N\) 根小木棍的长度。
输出格式:
一个数,表示要求的原始木棍的最小可能长度
思路
搜索剪枝
搜索的思路就是枚举原来木棍的长度,这个长度应该是分布在 $ [ max{A_i} , \frac{\sum_{i=1}^N{A_i}}{2}]$ 之间,而且这个长度应该是 $ \sum_{i=1}^N{A_i} $的因子
然后搜索里就是枚举木棍组合看是否可行
剪枝的策略:
1、如果搜索过程中发现当前组合无论加哪一根木棍都会超过目标长度,剪
2、如果在将某一个长度的小木棍添加到组合中失败后,对于这个组合,这一长度的木棍都不用试了
3、第一次搜索拓展时只用拓展一号节点
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 66
int nums[MAXN];
bool vis[MAXN];
int i,j,k,m,n,sum,ans;
bool flag;
bool cmp(const int &a,const int &b){
return a>b;
}
inline void check(int cur,int cnt,int goal,int pos){
if(cnt*goal==sum){
printf("%d\n",goal);
exit(0);
}
if(cur==goal){
check(0,cnt+1,goal,1);
return;
}
if(goal-cur<nums[n]) return;
for(register int i=pos;i<=n;i++)
if(!vis[i]&&cur+nums[i]<=goal){
vis[i]=true;
check(cur+nums[i],cnt,goal,i+1);
vis[i]=false;
if(cur+nums[i]==goal||cur==0) break;
while(nums[i]==nums[i+1]) i++;
}
}
int main(){
memset(vis,0,sizeof(vis));
scanf("%d",&m);
n=sum=0;
for(i=1;i<=m;i++){
scanf("%d",&k);
if(k<=50) nums[++n]=k,sum+=k;
}
sort(nums+1,nums+n+1,cmp);
ans=sum;
for(i=nums[1];i<=sum/2;i++){
if(sum%i==0) check(0,0,i,1);
}
printf("%d\n",ans);
return 0;
}