洛谷P1120 小木棍

Posted H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P1120 小木棍相关的知识,希望对你有一定的参考价值。

  题目传送门

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

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

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

输入输出格式
输入格式:
输入文件共有二行。

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

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

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

输出格式:
输出文件仅一行,表示要求的原始木棍的最小可能长度

输入输出样例
输入样例#1:
9
5 2 1 5 2 1 5 2 1
输出样例#1:
6
说明
2017/08/05

数据时限修改:

-#17 #20 #22 #27 四组数据时限500ms

-#21 #24 #28 #29 #30五组数据时限1000ms

其他时限改为200ms(请放心食用)


 

  分析:CL大佬讲课的时候讲到了这题,于是就来做一下。但是讲的时候没听,于是做的时候遇到了瓶颈,还是参考了five20大佬的博客。确定了搜索的思路,那么要考虑的就是如何剪枝,这题真的是一道练习搜索剪枝的绝(e)世(xin)好(gui)题。总共需要用到一下几种剪枝:

  1,搜索范围:很显然,答案肯定是在[maxn,sum]内,maxn为给定木棍的最大长度,sum为给定木棍长度之和,同时正确答案一定能被sum整除,所以搜的时候范围就确定在maxn到sum/2之间能把sum整除的所有整数。如果在该范围内均不满足,那么答案就是sum。

  2,搜索顺序:由上面得出的范围可知,从大到小搜索更优,而且根据题目可知,木棍长度<=50,那么也不需要快排了,直接桶排,记录每种长度的木棍的数目即可。

  3,如果搜索过程中已经使用过长度为x的木棍,那么下一层搜索直接从x开始从大到小搜,因为显然此刻使用了x那么比x长的都已经不可行了。

  4,如果某次搜索时如果当前长度为0或当前长度加选择木棍的长度等于目标长度,那么搜完以后直接break,因为是从大到小枚举,因此再小的木棍就不能达到目标长度了。(这个优化可能这么说并不太好理解,结合代码应该就好懂了)

  Code:

  

//It is made by HolseLee on 19th Apr 2018
//Luogu.org P1120
#include<bits/stdc++.h>
using namespace std;
const int N=101;
int n,num[N],sum,cnt;
int maxn=0,minn=N;
inline void dfs(int tar,int now,int len,int m)
{
  if(!tar){printf("%d",len);exit(0);}
  if(now==len){
    dfs(tar-1,0,len,maxn);
    return;}
  for(int i=m;i>=minn;i--){
    if(now+i<=len&&num[i]){
      num[i]--;
      dfs(tar,now+i,len,i);
      num[i]++;
      if(!now||now+i==len)
    break;}
  }
}
int main()
{
  ios::sync_with_stdio(false);
  cin>>n;int x;
  for(int i=1;i<=n;i++){
    cin>>x;
    if(x<=50){
      num[x]++;cnt++;
      maxn=max(maxn,x);
      minn=min(minn,x);
      sum+=x;}
  }
  for(int i=maxn;i<=sum/2;i++){
    if(sum%i==0)dfs(sum/i,0,i,maxn);
  }
  printf("%d",sum);
  return 0;
}

 

以上是关于洛谷P1120 小木棍的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1120 小木棍

P1120小木棍

P1120 小木棍 [数据加强版] 题解

P1120 小木棍 [数据加强版]

题解 P1120 小木棍 [数据加强版]

luogu P1120 小木棍 [数据加强版]