yzoi1777倒水问题的详细解法
Description - 问题描述
输入只有一行,即4个整数:
xMax yMax zMax n
其中100>xMax>yMax>zMax,n<100
Output - 输出数据
对于每个测试数据,输出其倒水步骤,如果步骤只有一步,step不加s请参考Sample Output,无解的话输出“No Solution”
算法分析
首先,我们需要考虑倒出者和接收者的可能分别为x->y,x->z(前提是x>0);y->x,y->z(前提是y>0);z->x,z->y(前提是z>0)共6种可能,接下来我们便对其进行详细分析。
①x->y时
前提:if(x>0),接着,我们要考虑的就是当将x中所有水倒入y中,是否已经超过了y的容量,代码如下
if(x>)
{
if(x+y>ymax)
{
x=x-(ymax-y);//将x中所有水倒入y中时已超出y的最大容量
y=ymax;
}
else
{
y=x+y;//将x中所有水倒入y中后未超过其最大容量
x=;//即x中的水全部倒入y中,x变为空
}
}
②x->z时
前提:if(x>0)这时,我们可以将x->z的过程类比成x->y的过程,其实际上的过程是一样的,代码如下:
if(x>)
{
if(x+z>zmax)
{
x=x-(zmax-z);//z容器倒满
z=zmax;
}
else
{
z=z+x;//x倒空
x=;
}
}
③y->x时
前提:if(y>0),这是,我们还要不要像以前那样考虑y会不会倒空,已及x会不会倒满呢?其答案是否定的,为什么呢?你可以设想:在整个装置(包括x,y以及z)中,其总水量之和为xmax。故不管y如何倒都不会使x>xmax,代码如下:
if(y>)
{
x=x+y;//y倒空
y=;
}
④y->z时,同x->y
⑤z->x时,同y->x
⑥z->y,这里也是同y->x吗?其实不然,当y->x时,无论如何都不会使x>xmax。但当z->y时却有可能使y>ymax,很多人都跳入了这个坑里,导致最终程序不能ac。经过仔细思考之后,其实可以知道这是和x->y一样的。代码如下:
if(z>)
{
if(z+y>ymax)
{
z=z-(ymax-y);
y=ymax;
}
else
{
y=y+z;
z=;
}
}
在对倒水的过程进行详细分析之后,最重要的便是算法的构造。仔细分析可以知道,这道题是要求我们分析倒水的每种情况,然后找出最优解。毫无疑问,这便是bfs的思路。可能有些人不了解或对bfs接触较少,这里给上百度对其的解释以助理解:
BFS一般指宽度优先搜索,宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。而在这个问题中,我们使用队列(queue)来实现之。
首先,假设我们已经定义了一个队列q,如下所示
struct node
{
int qx,qy,qz;
int pre;
} q[maxn];
其中qx,qy,qz表示当前各个装置中水的含量,而对于pre,相信大多数人都会纳闷:这是用来干什么的呢?其实,当你仔细阅读题目便可知道,它不仅要求输出最少次数,还得打印出每倒一次水的步骤,这里的pre便是用以存储当前状态所对应的上一个状态,到最后输出时,只需沿着pre进行输出即可。
void push()
{
bool can_push=true;
for(int i=;i<=rear;i++)
{
if(q[i].qx==x&&q[i].qy==y&&q[i].qz==z)
{
can_push=false;
return;
}
}
if(can_push)
{
rear++;
q[rear].qx=x;
q[rear].qy=y;
q[rear].qz=z;
q[rear].pre=front;
}
}
同时,在倒水时如果先前已经出现了与当前状态一致的状态,那还要不要继续倒呢?显然,答案是否定的,而这里的bool型变量can_push正是帮助完成了这一点.
在倒完水后,我们还需判断是否达到了目标状态,如果达到了就跳出循环,然后输出过程,代码如下:
void is_target()
{
if(q[rear].qx==n||q[rear].qy==n||q[rear].qz==n)
flag=true;
else
flag=false;
}
细心的人可能会发现:在模拟倒水过程时的x,y,z三个变量是哪来的呢?以是,我们还得写个取队首元素的函数,代码如下;
void initial()
{
x=q[front].qx;
y=q[front].qy;
z=q[front].qz; }
最后,我们再将刚刚模拟过的6种情况写下,再加一个while循环便可完成任务。接下来还有一点值得思考:这里的while循环的条件是什么。还记得题目中给出的“No Solution”吗。是的,在最后跳出循环后我们还得加一条is_target()(其实在跳出循环之前已经执行了一次is_target(),故只需判断flag即可)以判断是否有解。经过这样的分析后,while执行的条件便是队列是否为空is_empty()。倒水总过程的代码如下:
void pour()
{
while(front <= rear)
{
initial();
//x->y
if(x>)
{
if(x+y>ymax)
{
x=x-(ymax-y);
y=ymax;
}
else
{
y=x+y;
x=;
}
}
push();
is_target();
if(flag)
break;
//x->z
initial();
if(x>)
{
if(x+z>zmax)
{
x=x-(zmax-z);
z=zmax;
}
else
{
z=z+x;
x=;
}
}
push();
is_target();
if(flag)
break;
//y->x
initial();
if(y>)
{
x=x+y;
y=;
}
push();
is_target();
if(flag)
break;
//y->z
initial();
if(y>)
{
if(y+z>zmax)
{
y=y-(zmax-z);
z=zmax;
}
else
{
z=y+z;
y=;
}
}
push();
is_target();
if(flag)
break;
//z->x
initial();
if(z>)
{
x=x+z;
z=;
}
push();
is_target();
if(flag)
break;
//z->y
initial();
if(z>)
{
if(z+y>ymax)
{
z=z-(ymax-y);
y=ymax;
}
else
{
y=y+z;
z=;
}
}
push();
is_target();
if(flag)
break;
front++;
}
}
还有一点要补充的就是这题也可以用STL里的queue来做,这样便可省去自己写队列的麻烦。
最后,给上OJ上ac的代码,仅供参考:
/*
Name: lwq
Copyright:
Author:
Date: 07/12/14 00:57
Description: 1777
*/ #include<iostream>
using namespace std;
const int maxn=+;
int front,rear;
int xmax,ymax,zmax;
int n;
int x,y,z;
int step=;
int k=;
bool flag=false;
struct node
{
int qx,qy,qz;
int pre;
} q[maxn];
struct node_data
{
int x,y,z;
}data[maxn];
void is_target()
{
if(q[rear].qx==n||q[rear].qy==n||q[rear].qz==n)
flag=true;
else
flag=false;
}
void initial()
{
x=q[front].qx;
y=q[front].qy;
z=q[front].qz; }
void push()
{
bool can_push=true;
for(int i=;i<=rear;i++)
{
if(q[i].qx==x&&q[i].qy==y&&q[i].qz==z)
{
can_push=false;
return;
}
}
if(can_push)
{
rear++;
q[rear].qx=x;
q[rear].qy=y;
q[rear].qz=z;
q[rear].pre=front;
}
}
void pour()
{
while(front <= rear)
{
initial();
//x->y
if(x>)
{
if(x+y>ymax)
{
x=x-(ymax-y);
y=ymax;
}
else
{
y=x+y;
x=;
}
}
push();
is_target();
if(flag)
break;
//x->z
initial();
if(x>)
{
if(x+z>zmax)
{
x=x-(zmax-z);
z=zmax;
}
else
{
z=z+x;
x=;
}
}
push();
is_target();
if(flag)
break;
//y->x
initial();
if(y>)
{
x=x+y;
y=;
}
push();
is_target();
if(flag)
break;
//y->z
initial();
if(y>)
{
if(y+z>zmax)
{
y=y-(zmax-z);
z=zmax;
}
else
{
z=y+z;
y=;
}
}
push();
is_target();
if(flag)
break;
//z->x
initial();
if(z>)
{
x=x+z;
z=;
}
push();
is_target();
if(flag)
break;
//z->y
initial();
if(z>)
{
if(z+y>ymax)
{
z=z-(ymax-y);
y=ymax;
}
else
{
y=y+z;
z=;
}
}
push();
is_target();
if(flag)
break;
front++;
}
}
int main()
{
front=,rear=;
cin>>xmax>>ymax>>zmax;
cin>>n;
q[front].qx=xmax;
q[front].qy=;
q[front].qz=;
pour(); if(flag)
{ while(q[rear].pre>)
{
k++;
data[k].x=q[rear].qx;
data[k].y=q[rear].qy;
data[k].z=q[rear].qz;
rear=q[rear].pre;
}
if(k>)
cout<<k<<' '<<"steps"<<endl;
else
cout<<k<<' '<<"step"<<endl;
cout<<"step0:"<<' '<<xmax<<' '<<''<<' '<<''<<endl;
for(int i=k;i>=;i--)
{
cout<<"step"<<k-i+<<':'<<' '<<data[i].x<<' '<<data[i].y<<' '<<data[i].z<<endl;
}
}
else
{
cout<<"No Solution"<<endl;
}
return ;
}
最后,还要感谢YYL大神的不啬指教以及xaero的ppt。
这是我的第一篇博文,欢迎各位大神的指点。
yzoi1777倒水问题的详细解法的更多相关文章
- yzoi2226最小步数的详细解法
Description - 问题描述 在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”.有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字. ...
- 蓝桥杯(Java方法、详细解法分析)基础练习 阶乘计算
问题描述 给定n和len,输出n!末尾len位. 输入格式 一行两个正整数n和len. 输出格式 一行一个字符串,表示答案.长度不足用前置零补全. 样例输入 6 5 样例输出 00720 数据规模和约 ...
- yzoi2223集合构造的详细解法
Description - 问题描述 集合M的定义如下: 1是M中的元素 如果x是M中的元素,那么2x+1和4x+5都是M中的元素 那么,集合M中,最小的n个数是哪些? Input - 输入数据 一个 ...
- IOS 关于property的详细解法
1.格式 @property (参数1,参数2,...) 类型 名字; eg: @property(nonatomic,retain) UIWindow *window; 其中参数主要分为三类: • ...
- 已知有两个水杯,一个11L一个7L,水可以任意使用,求怎么得到2L 的详细解法
问题:有两个水杯,一个是11L一个是7L,水可以随便用,怎么得到2L 1.了解问题的本质 问题中给出了两个杯子,只有这两个杯子有量度,所以只能让杯中的水满进满出才能确定杯子中最后有多少水. 现在问题要 ...
- LeetCode 861翻转矩阵后得分详细解法
1. 题目内容 有一个二维矩阵 A 其中每个元素的值为 0 或 1 . 移动是指选择任一行或列,并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0. 在做出任意次数的移动后 ...
- Bug #19528825 "UNABLE TO PURGE A RECORD"
概述: 在生产环境中,当开启insert buffer时(参数innodb_change_buffering=all),部分实例偶尔会出现“UNABLE TO PURGE A RECORD”错误.这个 ...
- tyvj P1716 - 上帝造题的七分钟 二维树状数组区间查询及修改 二维线段树
P1716 - 上帝造题的七分钟 From Riatre Normal (OI)总时限:50s 内存限制:128MB 代码长度限制:64KB 背景 Background 裸体就意味着 ...
- UVA 10911 Forming Quiz Teams(dp + 集合最优配对问题)
4th IIUC Inter-University Programming Contest, 2005 G Forming Quiz Teams Input: standard input Outpu ...
随机推荐
- Performance testing of web application
Testing the performance of web application is easy . It's easy to design unrealistic scenario . Easy ...
- 游戏开发设计模式之子类沙盒模式(unity3d 示例实现)
积累提供所有操作(的实现)来定义子类的行为用一个最简单的例子来讲解这个模式玩家操纵的英雄也就是这个游戏的主角会有许多技能,我们想定义许多不同的技能,来让玩家使用.首 先我们定义一个skillBase类 ...
- Unity-layermask的问题
using UnityEngine; using System.Collections; public class NewBehaviourScript : MonoBehaviour { priva ...
- effective C++ 读后笔记
首先不得不说侯捷翻译的书大部分我都很喜欢,因为侯捷本身是一名出色的C++技术专家.这本书讲的是C++如何高效的运行,我想要成为一名卓越的开发人员,代码的高效性是必不可少的.很多人的代码质量很差,即使能 ...
- mapreduce 读写lzo文件
1.读lzo文件 需要添加以下代码,并导入lzo相关的jar包 job.setInputFormatClass(LzoTextInputFormat.class); 2.写lzo文件 lzo格式默认 ...
- poj 2462 Period of an Infinite Binary Expansion
欧拉定理.根据分数转换成2进制的过程,分子每次都乘2.对于循环节x,当2^x = 1(mod b)时肯定是循环节.显然当分母不能整除2的时候,即分母和2互质的话,就可以利用欧拉定理,使得2^(Eule ...
- string字符串转成16进制
package util; public class EscapeUnescape { public static String escape(String src) { int i; char j; ...
- OPENSHIFT MYSQL使用Navicat连接配置
最近一直在研究openshift免费空间的使用,对于如何用本地的数据库工具连接一直在找方法,呵呵,初学者. 以下方法可以实现: 1.在本地命令行中执行命令:rhc port-forward 应用名 如 ...
- x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1
文章文件夹: x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1 x264 编码器选项分析 (x264 Codec Strong and Weak P ...
- [转] 使用Git进行小项目代码管理
http://www.uml.org.cn/pzgl/201206155.asp 之前在公司使用过SVN(无甚感觉)和ClearCase(把人恶心死的东西)两种版本控制工具,都不满意.后来想自己写点东 ...