【蓝桥杯/算法训练】Sticks 剪枝算法
剪枝算法
大概理解是通过分析问题,发现一些判断条件,避免不必要的搜索。通常应用在DFS 和 BFS 搜索算法中;剪枝策略就是寻找过滤条件,提前减少不必要的搜索路径。
Sticks
问题描述
George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
输入格式
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
输出格式
The output should contains the smallest possible length of original sticks, one per line.
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
6
5
问题分析
题目意思:
乔治开始拿了a个长度均为L的木棍,随机锯木棍,最终把这a个木棍锯成了n个小块。根据n个小块及其各自长度,求出L可能的最小值。
如上图,根据右边的结果求出左边的L最小值为6(为什么要求最小值?因为L还可能为12,48)
剪枝条件分析:
1.a为整数,故初始长度L一定是总长度sum的因数。
2.n个小木块中,长度长的木块凑成L长的搭配可能性更小,所以将这n个木块从大到小排序,从大的开始搜索
3.搜索过程总按照排序,前一个小木棍没有成功时这一个也一定不能成功,直接略过
4.当一个木块拼凑失败时,直接回溯,不再按照这个条件搜索
#include<bits/stdc++.h>
using namespace std;
#define MAX 65536
int a[MAX];
int vis[MAX];//是否已匹配的标志
int maxed;
int len; //木棍的数量
bool cmp(int x,int y)
{
return x>y;
}
//通过dfs判断是否可以拼凑成长度为k的木块
bool dfs(int sum,int cur,int res,int k) //k为假设的单个木块长度 ,res为已拼成木棍的数量 ,sum为当前正在拼凑的这一根木棍已有长度
{
if(res==maxed) //已成功拼完返回成功
{
return true;
}
for(int i=cur;i<len;i++) //len为切割后的木棍数量,一个个遍历 ,cur为目前搜索位置,cur前的为不符合条件的
{
//如果第i个木棍已计入,或者与前一个木棍等长且同样未计入(未计入表示方案不可行,所以这一个不用搜了,也不行)
if(vis[i]||(i&&a[i]==a[i-1]&&!vis[i-1]))
{
continue;
}
if(a[i]+sum==k) //成功拼成一个假定长度的小木块
{
vis[i] =1;
if(dfs(0,0,res+1,k)) //res+1即成功拼凑数量加一,开始拼下一个,sum,cur初始化为0
return true;
vis[i]=0; //表示虽然这一步拼成了长度为k的木块,但后面剩下的木块不能成功拼凑,故失败回溯
return false;
}
if(a[i]+sum<k) //没拼好
{
vis[i]=1;
if(dfs(a[i]+sum,i+1,res,k)) //递归继续循环将之拼为假设长度
return true;
vis[i]=0;
if(!sum)
return false; //最后仍不能拼成
}
}
return false;
}
int main()
{
int sum; //木棍总长度
while(cin>>len&&len)
{
sum=0;
for(int i=0;i<len;i++)
{
cin>>a[i];
sum+=a[i];
}
sort(a,a+len,cmp); //将木棍长度从大到小排序
int flag=0;
for(int i=a[0];i<=sum/2;i++)
{
if(sum%i==0) //可能的木棍的单独长度为sum的因子(因为初始为等长的完整的木棍
{
memset(vis,0,sizeof(vis)); //标记是否已用
maxed=sum/i; //可能的最多木棍数
if(dfs(0,0,0,i))
{
cout<<i<<endl;
flag=1;
break;
}
}
}
if(!flag)
{
cout<<sum<<endl;
}
}
return 0;
}
例子运行过程
eg:
输入:
9
5 2 1 5 2 1 5 2 1
输出:
6
代码转载自该博客
胜利大逃亡
题目描述:
Ignatius被魔王抓走了,有一天魔王出差去了,这可是Ignatius逃亡的好机会.魔王住在一个城堡里,城堡是一个ABC的立方体,可以被表示成A个B*C的矩阵,刚开始Ignatius被关在(0,0,0)的位置,离开城堡的门在(A-1,B-1,C-1)的位置,现在知道魔王将在T分钟后回到城堡,Ignatius每分钟能从一个坐标走到相邻的六个坐标中的其中一个.现在给你城堡的地图,请你计算出Ignatius能否在魔王回来前离开城堡(只要走到出口就算离开城堡,如果走到出口的时候魔王刚好回来也算逃亡成功),如果可以请输出需要多少分钟才能离开,如果不能则输出-1.
输入:
输入数据的第一行是一个正整数K,表明测试数据的数量.每组测试数据的第一行是四个正整数A,B,C和T(1<=A,B,C<=50,1<=T<=1000),它们分别代表城堡的大小和魔王回来的时间.然后是A块输入数据(先是第0块,然后是第1块,第2块......),每块输入数据有B行,每行有C个正整数,代表迷宫的布局,其中0代表路,1代表墙。
输出:
对于每组测试数据,如果Ignatius能够在魔王回来前离开城堡,那么请输出他最少需要多少分钟,否则输出-1.
样例输入:
1
3 3 4 20
0 1 1 1
0 0 1 1
0 1 1 1
1 1 1 1
1 0 0 1
0 1 1 1
0 0 0 0
0 1 1 0
0 1 1 0
样例输出:
11
分析问题可知,该题即寻找出逃路径,可以看作查找。
查找空间:从点(0,0,0)
到点(A-1,B-1,C-1)
的合法路径
查找目标:在查找空间中的所有路径中寻找一条最短的路径,即步数最少的路径
查找方法:可以看作回溯,走不通了就回退。
可以定义一个时刻的状态,(x,y,z,t)即由起点到(x,y,z)经历的最短时间t;各个状态按照其被查找的顺序依次转移扩展,使用队列。
该例子使用广度优先搜索的办法,但是如果全部搜完,则复杂度太大,如果仅需十步则搜索过程也达到了6^10
。所以需要剪枝,寻找减少搜索的条件;
分析题目可知,剪枝条件:
- 当遇到墙时不能走,则通过墙这一点的后面也均不用搜索;
- 当搜索时走到立方体外则不用搜索;
- 若该坐标已经被标记搜索过了,则不用再次搜索,因为每走一步时间会增加,所以后来经过该点的路线所费时间一定比第一次经过标记时的时间长,所以后来的均不用搜索;
结合广度优先搜索的实现方法:队列,可以在遍历中丢弃很多“枝”,故方法可行;
我看问题时的几个疑惑:
- 如何保证已经搜索的路线(即按照上述剪枝后)是最短路线?
这个应该是由剪枝条件三保证的,因为每个点被标记是否搜索过时是第一次经过此点,是从起点到此点的最短距离。如果绕路后过来则直接舍弃了(时间比第一次来长); - 计时和路线如何匹配,因为是立方体总觉得用…三维数组?可能是我思维太不灵活了…
点的多属性(三维坐标、从起点到该点的时间)用结构体表示。 - 最开始想着重复的点如果走完周围的六个点又走回去了怎么办?
也是因为我思维不灵活导致的……就是加个mark数组标记一下的问题
#include<bits/stdc++.h>
using namespace std;
#define MAX 51
#define MAXT 1001
struct N{
int x,y,z;
int t;
};
queue<N> q;
int maze[MAX][MAX][MAX];
bool mark[MAX][MAX][MAX];
int go[][3]={
1,0,0,
-1,0,0,
0,1,0,
0,-1,0,
0,0,1,
0,0,-1
};
int main()
{
freopen("data.txt","r",stdin);
int A,B,C,T;
cin>>A>>B>>C>>T;
for(int i=0;i<A;i++)
{
for(int j=0;j<B;j++)
{
for(int k=0;k<C;k++)
{
cin>>maze[i][j][k];
mark[i][j][k]=false;
}
}
}
N tn;
tn.x=0;
tn.y=0;
tn.z=0;
tn.t=0;
q.push(tn);
int flag=-1;
while(!q.empty())
{
N tmp=q.front();
q.pop();
for(int i=0;i<6;i++)
{
int tx=tmp.x+go[i][0];
int ty=tmp.y+go[i][1];
int tz=tmp.z+go[i][2];
int tt=tmp.t+1;
if(mark[tx][ty][tz]) continue;
if(maze[tx][ty][tz]) continue;
if(tx<0||ty<0||tz<0||tx>=A||ty>=B||tz>=C) continue;
if(tx==A-1&&ty==B-1&&tz==C-1)
{
if(tt<=T)
{
cout<<tt<<endl;
flag=1;
break;
}
}
N tmp2;
tmp2.x=tx;
tmp2.y=ty;
tmp2.z=tz;
tmp2.t=tt;
q.push(tmp2);
mark[tx][ty][tz]=true;
}
}
if(flag==-1)
{
cout<<flag<<endl;
}
return 0;
}
【蓝桥杯/算法训练】Sticks 剪枝算法的更多相关文章
- 蓝桥杯如何训练?(附VIP题库)
https://www.dotcpp.com/ 给大家介绍下蓝桥杯,是近几年可以说国内名气最大的程序设计类比赛了 相比国际赛事ACM,蓝桥杯入门简单.中文答题.拿奖率高,更适合国内大众化参加,近几年不 ...
- 蓝桥杯-入门训练 :Fibonacci数列
问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1.当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. ...
- 蓝桥杯 入门训练 Fibonacci数列(水题,斐波那契数列)
入门训练 Fibonacci数列 时间限制:1.0s 内存限制:256.0MB 问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非 ...
- 蓝桥杯 入门训练 Fibonacci数列
入门训练 Fibonacci数列 时间限制:1.0s 内存限制:256.0MB 问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. ...
- 蓝桥杯入门训练-Fibonacci数列
刚刚开始刷题的时候就栽了个大跟头,稍微记一下...... 一开始不是很理解:“我们只要能算出这个余数即可,而不需要先计算出Fn的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数 ...
- 蓝桥杯 入门训练 Fibonacci数列 解析
问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n ...
- 蓝桥杯-入门训练 :A+B问题
问题描述 输入A.B,输出A+B. 说明:在“问题描述”这部分,会给出试题的意思,以及所要求的目标. 输入格式 输入的第一行包括两个整数,由空格分隔,分别表示A.B. 输出格式 输出一行,包括一个整数 ...
- C++-蓝桥杯-入门训练
Fibonacci数列,快速幂 #include <cstdio> ][];}; ,MOD=; Matrix A,B,O,I; Matrix Mul(Matrix A,Matrix B){ ...
- 蓝桥杯算法训练 java算法 表达式求值
问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例输出 - ...
随机推荐
- GYCTF Flaskapp[SSTI模板注入 ]
题目复现传送门 学习链接: 找了个师傅的blog先学习一下基础的flask知识 https://www.freebuf.com/column/187845.html(从零学flask) 简单记录一下: ...
- CSS实现同一行中图片和文本垂直居中
1.为图片和文本都设置vertical-align:middle
- 重启监听卡在connecting to的问题
问题描述:lsnrctl start启动监听起不来,一直卡在connecting to半天 1.[oracle@orcl ~]$ lsnrctl start 一直卡半天,就是连不上,按照以前的解决办法 ...
- python高级应用
一:错误和异常 1.python错误和异常 语法错误:Python的语法错误或者解析错. 异常:运行时检测到的错误被称为异常. 2.异常处理 Python中使用try except 语句来捕获并处理异 ...
- 分布式集群HA模式部署
一:HDFS系统架构 (一)利用secondary node备份实现数据可靠性 (二)问题:NameNode的可用性不高,当NameNode节点宕机,则服务终止 二:HA架构---提高NameNode ...
- react-native-----hello word!
react-native运行helloword 今天是个特殊的时刻,我前天开始学习react-native,一直环境塔建出错,运行打包出错,今晚,我终于把这个难搞的环境给搭建好了,并成功运行了第一个h ...
- jQuery使用ajax跨域请求获取数据
jQuery使用ajax跨域请求获取数据 跨域是我在日常面试中经常会问到的问题,这词在前端界出现的频率不低,主要原因还是由于安全限制(同源策略, 即JavaScript或Cookie只能访问同域下的 ...
- LaTeX技巧006:使用pdfLaTeX时,添加PDF文件属性的方法
PDF文件中含有标题.主题.作者.关键字等属性.这些属性,在Acrobat Reader或者Foxit Reader中可以通过”文件”菜单下的”属性”查看,在Acrobat Read中还可以使用Ctr ...
- VNote笔记本和画图
VNote笔记本 跨平台的,以markdown标记语言记录的文本文档.从sourceforget.org开源网址下载即可. 画图集成: 1.集成graphviz http://www.graphviz ...
- 1080 Graduate Admission
大致题意就是有N个学生,有M个学校,每个学校的名额都是正整数.每个学生可以填K个学校志愿,N个学生一起排名以后,排名高的学生先挑学校,不保护一志愿. 题目要求: 首先,把所有学生按总成绩SUM(GE+ ...