P1120小木棍

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 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);		
    }
    
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值