题目传送

一道特别毒瘤能提醒人不要忘记剪枝的题。

首先不要忘了管理员的话。忘把长度大于50的木棍过滤掉真的坑了不少人(包括我)。

显然是一道DFS题 。考虑剪枝。

找找搜索要面临的维度、状态:原始木棍的长度len,原始木棍的条数m,当前正在拼第k条原始木棍、还剩下没拼完的长度rest,木根的编号。

去等效冗杂考虑:

  1、为了祛除冗杂,我们可以假设拼成一根原始木棍的若干小木棍的长度x1,x2,x3,x4,x5...满足x1<x2<x3<x4<x5<...同时把小木棍的长度从大到小排个序,每次找符合条件的小木棍时都从上一个符合条件的小木棍的后面去找,这样就不会有拼成小木棍长度的序列的元素相同,只因为顺序不同就重复搜索的情况了。

  2、注意可能有长度相同的小木棍,当我们试完一个小木棍后,再试长度跟它相同的小木棍时,显然得到的情况是一样的,所以应该试下一个长度不同的小木棍,即跳过长度与它相同的小木棍(这里我用预处理处理出每个小木棍的长度与它相等的最后一根小木棍的编号)。

可行性剪枝考虑:

  1、一个非常暴力的思路是从0开始到所有木根长度的总和sum枚举len搜索。显然超时妥妥的,所以考虑优化。发现m一定是个整数,所以len一定是sum的因数。而len只需枚举到sum/2就可以了。因为如果len枚举到sum/2后都没找到答案,那答案只能是所有小木棍拼成一个大木棍的长度。

  2、当k==m时,剩下的小木棍的长度总和等于len,即一定能拼成一条原始木棍,所以直接回溯即可。

  3、当resti(当前找到的小木棍为i时的rest)正好等于找到的符合条件的小木棍i的长度、并把它拼上,之后却不能把所有原始木棍拼出,那么resti在接下来拼木棍时肯定不能得到答案,所以直接回溯。因为resti拼完时,要么使用小木棍i,要么用几根长度和等于小木棍i的长度的小木棍才能刚好把resti拼完。本来那几根小木棍就不能与剩下的小木棍拼出还要拼的原始木棍,当那几根小木棍换成总长度一样,但个数为1的小木棍i后,不就更不能拼出还要拼的原始木棍了吗?

最优性剪枝考虑:找到答案后立即回溯,其他的操作都不管。

实现优化:

  1、当我们在找接下来要用哪条木棍拼时可以用二分查找在第last(一条符合条件的小木棍的编号)+1条到最后一条木棍中第一条长度小于等于rest的木棍。

  2、我们在每次回溯时把used数组恢复,而避免用memset(memset用多了,慢到崩溃)

AC代码:

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype> using namespace std; bool used[],bj; int a[],n,sum,m,len,nxt[],ans; char ch; bool cmp(int a,int b)
{
return a>b;
} inline int read()//吓得都用快读了
{
ans=;
ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) ans=(ans<<)+(ans<<)+ch-'',ch=getchar();
return ans;
} void dps(const int &k,const int &last,const int &rest)
{
if(k==m) {bj=;return;}//可行性剪枝2
if(!rest)
{
for(int i=;i<=n;i++)
if(!used[i])
{
used[i]=;
dps(k+,i,len-a[i]);
used[i]=;//别忘了回溯,因为可能影响到搜索树的“舅舅节点”。
return;
}
}
int l=last+,r=n,mid;
while(l<r)//
{
mid=(l+r)>>;
if(a[mid]<=rest) r=mid;
else l=mid+;
}
for(int i=l;i<=n;i++)
if(!used[i])
{
used[i]=;
dps(k,i,rest-a[i]);//可行性剪枝3
if(bj) return;//最优性剪枝
used[i]=;
if(rest==a[i]) return;
i=nxt[i];
}
} int main()
{
n=read();
int tot=;
int b;
for(int i=;i<=n;i++)
{
b=read();
if(b<=)
{
a[++tot]=b;
sum+=b;
}
}
n=tot;
sort(a+,a+n+,cmp);
for(int i=n;i>=;i--)//预处理
{
if(a[i]==a[i+]) nxt[i]=nxt[i+];
else nxt[i]=i;
}
int s=sum/;used[]=;
for(len=a[];len<=s;len++)
if(sum%len==)//可行性剪枝1
{
bj=;
m=sum/len;
dps(,,len-a[]);
if(bj)
{
printf("%d",len);
return ;
}
}
if(!bj) cout<<sum;
return ;
}

一本通&&洛谷——P1120 小木棍 [数据加强版]——题解的更多相关文章

  1. 洛谷 P1120 小木棍 [数据加强版]解题报告

    P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...

  2. 洛谷——P1120 小木棍 [数据加强版]

    P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...

  3. 洛谷 P1120 小木棍 [数据加强版]

    P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...

  4. 洛谷—— P1120 小木棍 [数据加强版]

    https://www.luogu.org/problem/show?pid=1120 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接 ...

  5. [洛谷P1120]小木棍 [数据加强版]

    题目大意:有一些同样长的木棍,被切割成几段(长$\leqslant$50).给出每段小木棍的长度,找出原始木棍的最小可能长度. 题解:dfs C++ Code: #include<cstdio& ...

  6. 洛谷P1120 小木棍 [数据加强版]搜索

    玄学剪支,正好复习一下搜索 感觉搜索题的套路就是先把整体框架打出来,然后再一步一步优化剪枝 1.从maxv到sumv/2枚举长度(想一想,为什么) 2. 开一个桶,从大到小开始枚举 3. 在搜索中,枚 ...

  7. P1120 小木棍 [数据加强版] 回溯法 终极剪枝

    题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度 ...

  8. P1120 小木棍 [数据加强版]

    题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编 ...

  9. P1120 小木棍 [数据加强版](poj 1011)

    题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编 ...

随机推荐

  1. 九、Zabbix-触发器

    1.触发器是用来触发报警,或这其他动作的机制,它需要依赖监控项,以监控项为基础创建 3.创建触发器 (1)配置—>模板—>需要调整的模板—>触发器 (2)编辑触发器

  2. 多线程18-QueueUserWorkItem

        ));         }         ;             ;             ));             ThreadPool.QueueUserWorkItem(A ...

  3. [多校联考2019(Round 5 T2)]蓝精灵的请求(二分图染色+背包)

    [多校联考2019(Round 5)]蓝精灵的请求(二分图染色+背包) 题面 在山的那边海的那边住着 n 个蓝精灵,这 n 个蓝精灵之间有 m 对好友关系,现在蓝精灵们想要玩一个团队竞技游戏,需要分为 ...

  4. shell学习笔记3---shell变量

    Shell变量的定义.赋值和删除 脚本语言在定义变量时通常不需要指明类型,直接赋值就可以,Shell 变量也遵循这个规则. 在 Bash shell 中,每一个变量的值都是字符串,无论你给变量赋值时有 ...

  5. 通过编写串口助手工具学习MFC过程——(七)添加Tab Control控件

    通过编写串口助手工具学习MFC过程 因为以前也做过几次MFC的编程,每次都是项目完成时,MFC基本操作清楚了,但是过好长时间不再接触MFC的项目,再次做MFC的项目时,又要从头开始熟悉.这次通过做一个 ...

  6. go & flag

    参考 Golang下的flag模块使用 Go基础篇[第6篇]: 内置库模块 flag

  7. HTML-图片和多媒体

    1.图片和多媒体 (1)    图片:img元素 src 属性:图片路径: alt 属性:图片无法显示时使用的替代文字: title:鼠标悬停时显示的文字 : <img src="图片 ...

  8. 流畅的Python (Fluent Python) —— 第二部分01

    2.1 内置序列类型概览 Python 标准库用 C 实现了丰富的序列类型,列举如下. 容器序列 list. tuple 和 collections.deque 这些序列能存放不同类型的数据. 扁平序 ...

  9. 026-Cinder服务-->使用NFS作为后端存储

    以下将介绍如何使用NFS共享作为Openstack后端存储,本案例在计算节点上配置nfs [root@linux-node2 ~]# yum install -y openstack-cinder p ...

  10. VB.NET Event RaiseEvent用处

    一.代码 Private Sub Form1_Load(ByVal sender As Object, _                       ByVal e As System.EventA ...