P1120 小木棍 [数据加强版]

题目描述

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

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

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

输入输出格式

输入格式:

共二行。

第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中 N≤65

(管理员注:要把超过 50 的长度自觉过滤掉,坑了很多人了!)

第二行为 N 个用空个隔开的正整数,表示 N 根小木棍的长度。

输出格式:

一个数,表示要求的原始木棍的最小可能长度

我的AC过程:

一开始蜜汁出错,然后改了改代码

这就非常尴尬了.......

肝了三个小时,无数剪枝,依然66分

然后去睡觉QAQ

第三天.......

重构代码,胡乱剪枝,然后.......

好了不废话了,讲一下思路和代码

思路:

dfs遍历几乎每一种情况

一找到可行解就输出并退出程序

然后五个剪枝,代码中会详细说到

代码解释:

​
​
#include <iostream>
#include <algorithm>
using namespace std;
int n,a[77],cnt,maxn,maxnn;
bool use[77];

bool cmp(int x,int y)
{return x>y;}

inline void dfs(int ans,int sum,int ii,int now)
//ans代表现在正在已经拼好的长度
//sum代表已经拼好的根数
//ii代表每一根原先是多长(也就是答案)
//now代表现在要试着将哪一根木棍拼上去
{
    if(sum*ii==maxn)//此时都拼好了,直接输出;
	{
	cout<<ii;
	exit(0);//可以在程序的任何地方退出程序;
	}
    if(maxn-ans<a[cnt])//此时剩余的长度连最短的一截也拼不上,回溯到上一层:剪枝1
	return;
    if(ans==ii)//已经拼好了这一跟,该拼下一根
	{
	dfs(0,sum+1,ii,1);
	return;
	}

    for(int i=now;i<=cnt;i++)//不需要从一遍历到cnt了,之前的已经遍历过了:剪枝2
        if(!use[i] && ans+a[i]<=ii)
		{
            use[i]=1;
            dfs(ans+a[i],sum,ii,i+1);
            use[i]=0;
            if(ans==0||ans+a[i]==ii)break;
            //若某组拼接不成立,且此时 已拼接的长度为0 或 当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案时,则可直接跳出循环。因为此时继续枚举其它更小的值时,显然可能情况更少,且同样凑不完。:剪枝3
          while(a[i]==a[i+1])i++;//如果其它木棍和拼接失败的木棍一样长,那么这根木棍也必然拼接失败:剪枝4
        }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
	{
        int x;
        cin>>x;
        if(x<=50)
		{
		a[++cnt]=x;
		maxn+=a[cnt];//总长度
		}
    }
    sort(a+1,a+cnt+1,cmp);
    for(int i=a[1];i<=maxn/2;i++)//剪枝5
    //这里为什么只需要循环到一半呢?
   //一个数的因数不可能超过本数一半(因数为本数除外)
    if(maxn%i==0)
	  dfs(0,0,i,1);
    cout<<maxn;//这里解决了因数为本数的特殊情况;
    return 0;
}

​

​

猜你喜欢

转载自blog.csdn.net/qq_39541141/article/details/81056207