洛谷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;
}
posted @ 2019-01-16 21:03  EternalBlue  阅读(142)  评论(0编辑  收藏  举报