乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 505050 。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:
共二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中 N≤65N≤65N≤65
(管理员注:要把超过 505050 的长度自觉过滤掉,坑了很多人了!)
第二行为 NNN 个用空个隔开的正整数,表示 NNN 根小木棍的长度。
输出格式:
一个数,表示要求的原始木棍的最小可能长度
输入输出样例
输入样例#1: 复制
9 5 2 1 5 2 1 5 2 1
输出样例#1: 复制
6
说明
2017/08/05
数据时限修改:
-#17 #20 #22 #27 四组数据时限 500ms500ms500ms
-#21 #24 #28 #29 #30五组数据时限 1000ms1000ms1000ms
其他时限改为 200ms200ms200ms (请放心食用)
终于写完了(还是看了大佬的题解,我真是太菜了┭┮﹏┭┮)
1.这题直接爆搜的话是有分的,然而我的爆搜都能写蛤,就是这个我犯了很多次的原因
- 重点重点:写dfs的时候不要在里面用全局变量的时候一定要小心,有时候会在你意想不到的地方把它改了,总之用全局变量就一个之前说过的原则:一一对应,改完后要及时改回来
2.就是关于这道题目的思路和优化
(1)我一开始用的是迭代加深,就是没次找木棍时假设第i根木棍由几个已经切完的木棍组成,但实际上迭代加深的意义不大,反而极大地加大了时间复杂度
(2)还是复杂搜索的老套路,先想好搜索的顺序(肯定是先搜大的再搜小的而且约向后应该越小,因为大的可组合较小)
(3)然后就是剪枝:
- 当每次找到stick[i]和tot相加等于mid时,就直接break(因为如果i后面同样能找到若干个组合起来等于stick[i]的木棍,肯定是将它们换成stick[i]更好)
- 同样的就是第i个原木棍刚刚开始组合时,找到第一根可用的木棍就可以break(因为这跟木棍一定时剩余木棍中最大的,如果它不能组合好那找后面的就没意义了)
- 本题可能会有很多木棍等长,这样再搜索时会重复搜索,所以可以跳过等长的
做到上面的优化就可以ac了,还有一些小优化,像判断剩余最短木棍是否比mid-tot长等等
咸鱼代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,mid,num,sum,flag[80],stick[80];
int cmp(const int&a,const int&b)
{
if(a>b) return 1;
return 0;
}
int dfs(int dep,int lac,int tot,int lab)
{
if(lac==dep+1) {printf("%d",mid);exit(0);}
if(tot==mid)
{
dfs(dep,lac+1,0,1);
return 0;
}
if(stick[num]>mid-tot) return 0;
for(int i=lab;i<=num;i++)
{
if(!flag[i]&&stick[i]+tot<=mid)
{
flag[i]=1;
dfs(dep,lac,tot+stick[i],i);
flag[i]=0;
if(tot==0||stick[i]+tot==mid) break;
while(stick[i]==stick[i+1])i++;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&mid);
if(mid<=50&&mid>0)
{
stick[++num]=mid;
sum+=mid;
}
}
sort(stick+1,stick+num+1,cmp);
for(int i=num;i>=1;i--)
{
mid=sum/i;
if(mid*i==sum&&stick[1]<=mid) dfs(i,1,0,1);
}
}