剪枝算法

大概理解是通过分析问题,发现一些判断条件,避免不必要的搜索。通常应用在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。所以需要剪枝,寻找减少搜索的条件;

分析题目可知,剪枝条件:

  1. 当遇到墙时不能走,则通过墙这一点的后面也均不用搜索;
  2. 当搜索时走到立方体外则不用搜索;
  3. 若该坐标已经被标记搜索过了,则不用再次搜索,因为每走一步时间会增加,所以后来经过该点的路线所费时间一定比第一次经过标记时的时间长,所以后来的均不用搜索;

结合广度优先搜索的实现方法:队列,可以在遍历中丢弃很多“枝”,故方法可行;

我看问题时的几个疑惑:

  1. 如何保证已经搜索的路线(即按照上述剪枝后)是最短路线?

    这个应该是由剪枝条件三保证的,因为每个点被标记是否搜索过时是第一次经过此点,是从起点到此点的最短距离。如果绕路后过来则直接舍弃了(时间比第一次来长);
  2. 计时和路线如何匹配,因为是立方体总觉得用…三维数组?可能是我思维太不灵活了…

    点的多属性(三维坐标、从起点到该点的时间)用结构体表示。
  3. 最开始想着重复的点如果走完周围的六个点又走回去了怎么办?

    也是因为我思维不灵活导致的……就是加个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 剪枝算法的更多相关文章

  1. 蓝桥杯如何训练?(附VIP题库)

    https://www.dotcpp.com/ 给大家介绍下蓝桥杯,是近几年可以说国内名气最大的程序设计类比赛了 相比国际赛事ACM,蓝桥杯入门简单.中文答题.拿奖率高,更适合国内大众化参加,近几年不 ...

  2. 蓝桥杯-入门训练 :Fibonacci数列

    问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1.当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n. ...

  3. 蓝桥杯 入门训练 Fibonacci数列(水题,斐波那契数列)

    入门训练 Fibonacci数列 时间限制:1.0s   内存限制:256.0MB 问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非 ...

  4. 蓝桥杯 入门训练 Fibonacci数列

      入门训练 Fibonacci数列   时间限制:1.0s   内存限制:256.0MB        问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. ...

  5. 蓝桥杯入门训练-Fibonacci数列

    刚刚开始刷题的时候就栽了个大跟头,稍微记一下...... 一开始不是很理解:“我们只要能算出这个余数即可,而不需要先计算出Fn的准确值,再将计算的结果除以10007取余数,直接计算余数往往比先算出原数 ...

  6. 蓝桥杯 入门训练 Fibonacci数列 解析

    问题描述 Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. 输入格式 输入包含一个整数n ...

  7. 蓝桥杯-入门训练 :A+B问题

    问题描述 输入A.B,输出A+B. 说明:在“问题描述”这部分,会给出试题的意思,以及所要求的目标. 输入格式 输入的第一行包括两个整数,由空格分隔,分别表示A.B. 输出格式 输出一行,包括一个整数 ...

  8. C++-蓝桥杯-入门训练

    Fibonacci数列,快速幂 #include <cstdio> ][];}; ,MOD=; Matrix A,B,O,I; Matrix Mul(Matrix A,Matrix B){ ...

  9. 蓝桥杯算法训练 java算法 表达式求值

    问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例输出 - ...

随机推荐

  1. Vue中进度条的使用

    1. 安装npm install --save nprogress 2.导入js和css import NProgress from 'nprogress'import 'nprogress/npro ...

  2. Markdown数学公式如何打出回归符号

    来源:https://blog.csdn.net/garfielder007/article/details/51646604 函数.符号及特殊字符 语法 效果 语法 效果 语法 效果 \bar{x} ...

  3. Wannafly挑战赛13 zzf的好矩阵 题解 答案解释

    Wannafly挑战赛13 zzf的好矩阵 题解 文章目录 Wannafly挑战赛13 zzf的好矩阵 题解 分析 结论1 结论2 结论3 C数组对应带子说明 空白长度论述 后续黑色长度论述 能&qu ...

  4. webpack代理解决跨域

    开了一个9000端口,又开了一个8881端口,在9000端口访问页面,数据接口是8881,这样就产生了跨域,如何解决? webpack中做如下配置: proxy: { '/api/*': { targ ...

  5. numpy包学习笔记

    导入 import numpy as np argsort() numpy中的排序函数 返回的是数组中从小到大的索引值 from numpy import * test=[5,2,3,4,1] pri ...

  6. LIS 51Nod 1134 最长递增子序列

    给出长度为N的数组,找出这个数组的最长递增子序列.(递增子序列是指,子序列的元素是递增的) 例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10.   Input 第1行:1个 ...

  7. 爬格子呀--IEEE极限编程大赛留念

    10.14,坐标:电子科技大学 24h,不间断的编程,感觉还是很爽的. 排名一般,但是这是开始,未来还很远. 题目举例1: 广袤的非洲大草原上,狮子居住在一个个的网格里,他们的势力范围会以曼哈顿路程的 ...

  8. 针对wordpress CMS的信息收集

    如果发现一个站点用的是wordpress管理系统的话, 可以试试默认的后台地址:/wp-admin/ 访问后自动跳转置 后台登录界面 用户名收集 :/?author=1 依次访问/author=1 , ...

  9. [SHOI2001] 小狗散步 - 二分图匹配

    考虑到每次与主人相遇之前最多只去一个景点,很容易转化为匹配问题 由于数据很小,我们不妨枚举每个相遇点间隙和每个景点,判断是否来得及,如果来得及就连边 沙雕题搞了二十来分钟,我是憨憨 #include ...

  10. Unable to create initial connections of pool. spring boot mysql

    Unable to create initial connections of pool. 在链接url里添加 将useSSL=true改为useSSL=false 只能说明服务器没有打开SSL功能