奥赛一本通 小木棍

题目描述

原题来自:CERC 1995

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

第一行为一个单独的整数 N 表示砍过以后的小木棍的总数。 第二行为 N 个用空格隔开的正整数,表示 N 根小木棍的长度。

输出格式

输出仅一行,表示要求的原始木棍的最小可能长度。

样例

输入数据 1

9
5 2 1 5 2 1 5 2 1

Copy

输出数据 1

6

Copy

数据范围与提示

1≤N≤60

#include<bits/stdc++.h>
using namespace std;
int n;
int a[70],vis[70];
int sum,res, ans;
bool cmp(int x,int y)
{
	return x > y;
}

int dfs(int len,int sta,int now)//(当前剩余的长度,搜索到第几根木棍,已经用了几次)
{
	int i,j;
	if(now == res)return 1;
	if(len == 0)//用完了进行下一次
		if(dfs(ans,1,now + 1))//每次都要从头开始搜索
			return 1;
	for(i = sta;i <= n;i ++)//挑选下一根木棍
	{
		if(!vis[i] && a[i] <= len)//该木棍之前还没有选过且长度合理
		{
			vis[i] = 1;//选上
			if(dfs(len - a[i],i + 1,now))//因为是从大到小挑选的,i + 1可以保证不会从小的挑到大的
				return 1;
			vis[i] = 0;//回溯
			if(ans == len)break;
			while(a[i] == a[i + 1])i ++;
			/*
				如果ans==len说明有比原长度更大的木棍出现,那就不可能塞得下了,所以可以剪枝
				去重剪枝 相同长度的不符合条件的则不需再搜索
			*/
		}
	}
	return 0;       
}

int main()
{
		cin >> n;
		int i,j;
		for(i = 1;i <= n;i ++)
		{
			cin >> a[i];
			sum += a[i];
		} 
		sort(a + 1,a + 1 + n,cmp);//先配大的,再配小的,因为小的作用更大
		for(j = a[1];j <= sum;j ++)
		{   
			if(sum % j)continue;//如果除不尽就跳过
			res = sum / j;//分成的根数
			ans = j;//更新答案
			if(dfs(ans,1,0))
			{
				cout << ans << endl;
			}
		}
	
}

posted @ 2022-09-24 21:16  zyc_xianyu  阅读(51)  评论(0编辑  收藏  举报