P1120

小木棍

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 \(50\)

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

第一行是一个整数 \(n\),表示小木棍的个数。
第二行有 \(n\) 个整数,表示各个木棍的长度 \(a_i\)

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1

9
5 2 1 5 2 1 5 2 1

样例输出 #1

6

提示

对于全部测试点,\(1 \leq n \leq 65\)\(1 \leq a_i \leq 50\)

时限是250ms!!!
这题我只能说离谱
剪枝:
1.从大到小排序
2.枚举的len要整除tot
3.拼每一根长木棒时 这次用的小木棒长度肯定不大于上次用的
4.很玄学的一个优化:如果我们回溯时发现nowlen+i==len即本来这次时符合要求的但却回溯了 说明前面的某根没满足要求 所以我们直接return
同理 如果发现nowlen==0却回溯了 说明怎么填都不行 直接退出循环
5.由于有多根相同长度的小木棒 多次枚举相同的没意义 所以直接用桶排
这上面几个缺一不可!我真的…
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int cnt[55],n,x,maxn,tot,m,minn=51;
void dfs(int step,int len,int nowlen,int prelen)
{
	if(step==tot/len+1)
	{
		cout<<len<<"\n";
		exit(0);
	}
	if(nowlen==len)
	{
		dfs(step+1,len,0,maxn);
		return ;
	}
	for(int i=prelen;i>=minn;i--)
	{
		if(cnt[i]&&nowlen+i<=len)
		{
			cnt[i]--;
			dfs(step,len,nowlen+i,i);
			cnt[i]++;
			if(nowlen==0||nowlen+i==len)break;
		}
	}
	return ;
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}

int main()
{
	ios::sync_with_stdio(false);
	n=read();
	for(int i=1;i<=n;i++)
	{
		x=read();
		if(x<=50)
		{
			tot+=x;
			cnt[x]++;
			maxn = maxn > x ? maxn : x ;   
            minn = minn < x ? minn : x ;
		}
	}
	int tmp=tot>>1;
	for(int i=maxn;i<=tmp;i++)
	{
		if(tot%i==0)
		{
			dfs(1,i,0,maxn);
		}
	}
	cout<<tot<<"\n";
	return 0;
}
posted @ 2023-01-16 18:17  PKU_IMCOMING  阅读(9)  评论(0)    收藏  举报