C++宽搜经典题——三个水杯(一题懂宽搜!)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++宽搜经典题——三个水杯(一题懂宽搜!)相关的知识,希望对你有一定的参考价值。

最近复习了一下宽搜,感觉其实搜索是一种精神,而这种精神就是不怕困难走到底,掌握这种精髓能很好地帮助您解决一些需要举例子的问题。

今天找了一道题,比较值得学习,希望通过这道题了解宽搜的套路。

题目来源:http://acm.nyist.net/JudgeOnline/problem.php?pid=21(在此感谢作者!)

顺便附上题目:

三个水杯

时间限制:1000 ms | 内存限制:65535 KB
难度:4

描述
给出三个水杯,大小不一,并且只有最大的水杯的水是装满的,其余两个为空杯子。三个水杯之间相互倒水,并且水杯没有标识,只能根据给出的水杯体积来计算。现在要求你写出一个程序,使其输出使初始状态到达目标状态的最少次数。

输入
第一行一个整数N(0<N<50)表示N组测试数据
接下来每组测试数据有两行,第一行给出三个整数V1 V2 V3 (V1>V2>V3 V1<100 V3>0)表示三个水杯的体积。
第二行给出三个整数E1 E2 E3 (体积小于等于相应水杯体积)表示我们需要的最终状态
输出
每行输出相应测试数据最少的倒水次数。如果达不到目标状态输出-1
样例输入
2
6 3 1
4 1 1
9 3 2
7 1 1
样例输出
3
-1

 


附上AC代码详解,可能看起来有点繁琐,其实并没有你想象中那么难,要理解,三个杯子互相倒水有6种情况!相信您能理解!
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node{ //结构体表示倒水过程中的状态
int a,b,c;
int step; //step表示到当前这个状态所需要的步骤数
}b,e; //b、e分别表示开始状态和目标状态
int v1,v2,v3;
bool f[105][105][105]; //f(flag)表示三个杯子的这种状态是否讨论过,可以判重
//宽搜其他题比如要用hash值也是为了避免重复判断状态,节省时间

int bfs(node b)
{
queue<node> q; //一般宽搜都用队列实现
q.push(b);
f[b.a][b.b][b.c]=true;

node cur;
while (!q.empty()) //6种倒水情况 :耐心,其实懂了一个其他都可以复制粘贴加一点小修改
{
cur=q.front();
if (cur.a==e.a && cur.b==e.b && cur.c==e.c) //搜索到了就返回,也算个剪枝
return cur.step;
q.pop();

if (cur.a>0 && cur.b<v2) //v1往v2中倒水
{
int t=min(cur.a,v2-cur.b); //重点理解 两个杯子中水的改变量倒水瓶中所有的水和被灌水瓶剩的部分中的最小值
node temp;
temp.a=cur.a-t;
temp.b=cur.b+t;
temp.c=cur.c;
temp.step=cur.step+1; //步数加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}

if (cur.a>0 && cur.c<v3) //v1往v3中倒水
{
int t=min(cur.a,v3-cur.c); //重点理解
node temp;
temp.a=cur.a-t;
temp.b=cur.b;
temp.c=cur.c+t;
temp.step=cur.step+1; //步骤数加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}

if (cur.b>0 && cur.a<v1) //v2往v1中倒水
{
int t=min(cur.b,v1-cur.a); //重点理解
node temp;
temp.a=cur.a+t;
temp.b=cur.b-t;
temp.c=cur.c;
temp.step=cur.step+1; //步骤数加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}

if (cur.b>0 && cur.c<v3) //v2往v3中倒水
{
int t=min(cur.b,v3-cur.c); //重点理解
node temp;
temp.a=cur.a;
temp.b=cur.b-t;
temp.c=cur.c+t;
temp.step=cur.step+1; //步骤数加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}

if (cur.c>0 && cur.a<v1) //v3往v1中倒水
{
int t=min(cur.c,v1-cur.a); //重点理解
node temp;
temp.a=cur.a+t;
temp.b=cur.b;
temp.c=cur.c-t;
temp.step=cur.step+1; //步骤数加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}

if (cur.c>0 && cur.b<v2) //v3往v2中倒水
{
int t=min(cur.c,v2-cur.b); //重点理解
node temp;
temp.a=cur.a;
temp.b=cur.b+t;
temp.c=cur.c-t;
temp.step=cur.step+1; //步骤数加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
}
return -1; //表示仍然找不到目标状态
}
int main()
{
int n;
cin>>n;
while (n--) //多次询问
{
cin>>v1>>v2>>v3;
b.a=v1; b.b=0; b.c=0; b.step=0; //初始化
cin>>e.a>>e.b>>e.c; //目标状态
memset(f,0,sizeof(f)); //每次都要将标记清零,故放在循环里
if (v1!=e.a+e.b+e.c) //因为这样是肯定找不到的
cout<<"-1"<<endl;
else cout<<bfs(b)<<endl; //否则宽搜
}
return 0;
}

因为首次分享,定有许多不尽人意之处,还请大家谅解!



























































































































































以上是关于C++宽搜经典题——三个水杯(一题懂宽搜!)的主要内容,如果未能解决你的问题,请参考以下文章

宽搜经典题之三——魔板+康托展开

c++ 广度优先搜索(宽搜)

NYOJ21.三个水杯-初始态到目标态的最少次数-经典BFS

POJ 2251宽搜

ybtoj 宽搜进阶B. 2.射击问题

[NOIP2002] 字串变换 宽搜+深度优化