【题解】洛谷P1120 小木棍(搜索+剪枝+卡常)
洛谷P1120:https://www.luogu.org/problemnew/show/P1120
思路
明显是搜索题嘛
但是这数据增强不是一星半点呐
我们需要N多的剪枝
PS:需要先删去超出50的木棍
首先我们可以想到枚举每个小木棍的长度来搜索
但是直接枚举肯定会超时的 所以我们想到优化剪枝
因为要组成木棍肯定要从被砍开的木棍中的最大值开始枚举到所有木棍总和长(只有一根木棍被砍开)
然而这样却还不是最优的剪枝 因为每根原始小木棍的长度一样 所以枚举长度的时候可以判断是否被总和整除
而且我们只需要枚举到总和的一半即可 因为如果分成两根或以下行不通的话 到最后只需要输出总长就行(只有一根木棍)
因为短的可以组合得比长的更灵活 因此我们可以把木棍从大到小排序之后再选择
进入DFS后我们需要判断如果当前枚举的这根木棍还需要凑的长度为0 且已经满足:凑出的根数已经足够 (凑出长棍的根数=所有木棍的长度之和/原始长度)
当DFS失败时 我们需要退出并换另外一根长度不同的木棍 因为一样长度的可能有很多 所以时间浪费了 我们需要在进入DFS之前预处理出所有木棍下一根不同长度的编号
可以根据木棍长度的单调性来二分找出第一个木棍长度不大于未拼长度rest
参考洛谷某dalao的难想却特别特别重要剪枝:
如果当前长棍剩余的未拼长度等于当前木棍的长度或原始长度,继续拼下去时却失败了,就直接回溯并改之前拼的木棍
解释:
当前长棍剩余的未拼长度等于当前木棍的长度时,当前木棍明显只能自组一根长棍,但继续拼下去却失败,说明这根木棍不能自组?!这根木棍不自组就没法用上了,所以不用搜更短的木棍了,直接回溯,改之前的木棍;
当前长棍剩余的未拼长度等于原始长度时,说明这根原来的长棍还一点没拼,现在正在放入一根木棍。很明显,这根木棍还没有跟其它棍子拼接,如果现在拼下去能成功话,它肯定是能用上的,即自组或与其它还没用的木棍拼接。但继续拼下去却失败,说明现在这根木棍不能用上,无法完成拼接,所以直接回溯,改之前的木棍。
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 70
int n,k,minn,sum,c,num,len;
int mood[maxn],nex[maxn];//nex是预处理编号
bool vis[maxn],flag;
int cinn()
{
int x=,f=;
char ch=;
while(!isdigit(ch))
f|=(ch=='-'),ch=getchar();
while(isdigit(ch))
x=(x<<)+(x<<)+(ch^),ch=getchar();
return f?-x:x;
}
void write(int x)
{
if(x<)putchar('-'),x=-x;
if(x>=)write(x/);
putchar(x%+'');
}
bool cmp(int a,int b)
{
return a>b;
}
void dfs(int p,int last,int rest)//p是当前已经拼倒第几根 last是上一根用的编号 rest是还要多长
{
int i;
if(!rest)//还需要0长度的木棍
{
if(p==num)//如果已经凑齐了所有木棍
{
flag=;
return;
}
for(i=;i<=n;i++)//如果还没凑齐 找一根还没用过的当第一根要用的
if(!vis[i]) break;
vis[i]=;
dfs(p+,i,len-mood[i]);//搜索下一根
vis[i]=;
if(flag) return;
}
int l=last+,r=n,mid;
while(l<r)//二分找编号
{
mid=(l+r)>>;
if(mood[mid]<=rest) r=mid;
else l=mid+;
}
for(i=l;i<=n;i++)//注意从第一根不同的编号开始而不是从1开始
{
if(!vis[i])
{
vis[i]=;
dfs(p,i,rest-mood[i]);
vis[i]=;
if(flag) return;
if(rest==mood[i]||rest==len) return;//最后一个剪枝
i=nex[i];//更换小木棍
if(i==n) return;
}
}
}
void read()
{
n=cinn();
while(n)
{
int x;
x=cinn();
if(x<=)//删去大于50的木棍
{
sum+=x;
mood[++k]=x;
}
n--;
}
n=k;
sort(+mood,+mood+n,cmp);//从大到小排序
minn=mood[];//木棍中的最大值是枚举长度的最小值
nex[n]=n;
for(int i=n-;i>;i--)//预处理编号
{
if(mood[i]==mood[i+]) nex[i]=nex[i+];
else nex[i]=i;
}
}
int main()
{
read();
for(len=minn;len<=sum/;len++)
{
if(sum%len==)//整除
{
num=sum/len;//小木棍根数
flag=;
vis[]=;
dfs(,,len-minn);//取了第一根
vis[]=;
if(flag)
{
write(len);//如果满足输出
return ;
}
}
}
write(sum);//只有一根木棍
}
【题解】洛谷P1120 小木棍(搜索+剪枝+卡常)的更多相关文章
- 洛谷P1120 小木棍 [搜索]
题目传送门 题目描述乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍 ...
- 洛谷 P1120 小木棍 dfs+剪枝
Problem Description [题目链接] https://www.luogu.com.cn/problem/P1120 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不 ...
- 洛谷P1120 小木棍 [数据加强版](搜索)
洛谷P1120 小木棍 [数据加强版] 搜索+剪枝 [剪枝操作]:若某组拼接不成立,且此时 已拼接的长度为0 或 当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案时,则可直接跳出循环.因为此时继续 ...
- 洛谷P1120 小木棍
洛谷1120 小木棍 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长 ...
- 洛谷 P1120 小木棍 [数据加强版]解题报告
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...
- 洛谷——P1120 小木棍 [数据加强版]
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...
- 洛谷 P1120 小木棍 [数据加强版]
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...
- 一本通&&洛谷——P1120 小木棍 [数据加强版]——题解
题目传送 一道特别毒瘤能提醒人不要忘记剪枝的题. 首先不要忘了管理员的话.忘把长度大于50的木棍过滤掉真的坑了不少人(包括我). 显然是一道DFS题 .考虑剪枝. 找找搜索要面临的维度.状态:原始木棍 ...
- 洛谷 P1120 小木棍[数据加强版]
这道题可能是我做过的数据最不水的一道题-- 题目传送门 这题可以说是神剪枝,本身搜索并不算难,但剪枝是真不好想(好吧,我承认我看了题解)-- 剪枝: 用桶来存储木棍 在输入的时候记录下最长的木棍和最短 ...
随机推荐
- Struts2 学习(二)
一.Struts2 配置文件 1.配置多个配置文件 在大部分应用里,随着应用规模的增加,系统中Action的数量也会大量增加,导致struts.xml配置文件变得非常臃肿. 为了避免struts.xm ...
- Selector#wakeup()
看thrift源码发现selector.wakeup()方法,通常在selector.select()后线程会阻塞.使用wakeup()方法,线程会立即返回.源码分析应该是用的线程中断实现的.下面是个 ...
- 简单的数据库连接池实例(java语言)
1.概述 频繁的创建和销毁数据库连接消耗非常多的系统资源,创建一个池子, 管理一定数量的连接,用的时候去池中取,用完了放回池中,这时比较通用的做法. 2.关键字 LinkedList synchro ...
- eclipse Java类 红色感叹号 commit失败
解决方法: 1.进入java类文件所在物理目录 (e:\workspace\myproject\...) 2. 删除多余的版本管理工具的文件或文件夹(如 .svn) 3. 刷新eclipse工程 4 ...
- SPOJ QTREE7
题意 一棵树,每个点初始有个点权和颜色 \(0 \ u\) :询问所有\(u,v\) 路径上的最大点权,要满足\(u,v\) 路径上所有点的颜色都相同 $1 u \(:反转\)u$ 的颜色 \(2 ...
- 关于github改名问题
不喜欢github显示的目录名字于是百度了下,更改过程,记录下来,方便日后查看! 首页右上角点击出来菜单,找到Settings按钮点击 左侧找到Account账号菜单点击 找到change usern ...
- mysql的一些sql用法
mysql中修改列名: alter table 表名 change abc def 列类型;比如 alter table student change pws psw char(10);
- 数据结构----线性表顺序和链式结构的使用(c)
PS:在学习数据结构之前,我相信很多博友也都学习过一些语言,比如说java,c语言,c++,web等,我们之前用的一些方法大都是封装好的,就java而言,里面使用了大量的封装好的方法,一些算法也大都写 ...
- C++ 11: function & bind 使用示例
#include <functional> #include <iostream> struct Foo { Foo(int num) : num_(num) {} void ...
- java 中linq 的使用方式 筛选 查找 去重
1.筛选 $.Enumerable.From(value).Where(function(x) {//value 为被操作的内容 return x.name == name;//第一个name为val ...