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;
}
此生无悔入OI 来生AK IOI

浙公网安备 33010602011771号