『ACM C++』HDU杭电OJ | 1415 - Jugs (灌水定理引申)
今天总算开学了,当了班长就是麻烦,明明自己没买书却要带着一波人去领书,那能怎么办呢,只能说我善人心肠哈哈哈,不过我脑子里突然浮起一个念头,大二还要不要继续当这个班委呢,既然已经体验过就可以适当放下了吧,用心在自己的研究上。晚上级会开完也就八点多了,开始打打题,今天在HDU杭电的ACM集训题看到一个奇葩的题,前来献上。
今日推荐:
《全球风暴》 一部宇宙航空和地球气候片的良心佳作,后期特效建模都是特别杠杠的大片,不会让你失望的哟,我已经三刷了哈哈哈。这部片在爱奇艺有上线,有兴趣的朋友可以看看鸭。
爱奇艺链接:https://www.iqiyi.com/v_19rr7pl8vg.html
------------------------------------------------题目----------------------------------------------------------
Jugs
Problem Description
You have two jugs, A and
B, and an infinite supply of water. There are three types of actions that you
can use: (1) you can fill a jug, (2) you can empty a jug, and (3) you can pour
from one jug to the other. Pouring from one jug to the other stops when the
first jug is empty or the second jug is full, whichever comes first. For
example, if A has 5 gallons and B has 6 gallons and a capacity of 8, then
pouring from A to B leaves B full and 3 gallons in A.
A problem is given
by a triple (Ca,Cb,N), where Ca and Cb are the capacities of the jugs A and B,
respectively, and N is the goal. A solution is a sequence of steps that leaves
exactly N gallons in jug B. The possible steps are
fill A
fill B
empty A
empty B
pour A B
pour B A
success
where
"pour A B" means "pour the contents of jug A into jug B", and "success" means
that the goal has been accomplished.
You may assume that the input you
are given does have a solution.
Input
lines each defining one puzzle. Input for each puzzle is a single line of three
positive integers: Ca, Cb, and N. Ca and Cb are the capacities of jugs A and B,
and N is the goal. You can assume 0 < Ca <= Cb and N <= Cb <=1000
and that A and B are relatively prime to one another.
Output
instructions from the list of the potential output lines which will result in
either of the jugs containing exactly N gallons of water. The last line of
output for each puzzle should be the line "success". Output lines start in
column 1 and there should be no empty lines nor any trailing spaces.
Sample Input
Sample Output
- fill B
- pour B A
- empty A
- pour B A
- fill B
- pour B A
- success
- fill A
- pour A B
- fill A
- pour A B
- empty B
- pour A B
- success
------------------------------------------------题目----------------------------------------------------------
(一) 原题大意:
你有两个杯子,A、B;你只有三种操作,(1)清空任何一个杯子 (2)当被子是空的时候可以填满任何一个杯子 (3)将某一杯的水倒到另一杯中(不会溢出计算)直到B杯达到目标数Aim C。
输入的A、B升数有要求,一定需要相邻互质。并且A小于B,且目标数Aim C要小于B即可。
题目好像想象挺容易,手写也好像能解出来,但就是电脑老是犯傻逼。扔HDU的OJ上老是显示超时,我看了一下时间限制也很足够啊:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
后来我发现坑在哪里了。
这道题我居然在一个小学课本的趣味题发现的,真的是,现在的小学益智题怕是很多都能改成编程题了,而且改了编程题之后你还不一定解的出来哈哈哈。
(二) 题目分析:
其实方法很简单,你们可以试一下下面这个步骤,是一定能得到结果的:
(1)装满A
(2)从A倒B
(3)如果B满了清空
基本以上三个步骤都能找打准确的结果。
这就是经典的“灌水定理”,这里提一下,在下面我会引出这个定理,理论上倒水的步骤是不唯一的,所以我就太在意样例。
然而在无数次交OJ的时候疯狂的WA超时,我终于从样例发现了不对劲。在其他OJ上是可以过的,但在HDU OJ好像并没有被智能处理化:
如果目标数C小于A,必须从A开始倒满。如果目标数C大于A,则必须从B开始倒满。原因是为了寻求最短步骤操作,拿样例来说,3 5 4,如果先倒A,那么你需要八步,而如果先到B,那么你需要六步,所以这道题杭电OJ是默认要求最短步骤了,题目并没有说,所以害我 一股脑热直接从A循环交,就错了。
(三) 代码分块:
首先:我先构造三个步骤出来:Fill、Pour、Empty
- void pour(bool x)
- {
- if(x)
- {
- if(cap_b + cap_a <= temp_b)
- {
- cap_b = cap_b + cap_a;
- cap_a = ;
- }
- else
- {
- cap_a = cap_a - (temp_b - cap_b);
- cap_b = temp_b;
- }
- printf("pour A B\n");
- }
- else
- {
- if(cap_b + cap_a <= temp_a)
- {
- cap_a = cap_b + cap_a;
- cap_b = ;
- }
- else
- {
- cap_b = cap_b - (temp_a - cap_a);
- cap_a = temp_a;
- }
- printf("pour B A\n");
- }
- }
- void fill(bool x)
- {
- if(x)
- {
- cap_a = temp_a;
- printf("fill A\n");
- }
- else
- {
- cap_b = temp_b;
- printf("fill B\n");
- }
- }
- void empty(bool x)
- {
- if(x)
- {
- cap_b = ;
- printf("empty B\n");
- }
- else
- {
- cap_a = ;
- printf("empty A\n");
- }
- }
其中x为真是以A为主,为假时是B为主操作。难点应该在于Pour倾倒函数的书写,你需要区分从A倒到B时,是否B满了,如果满了就不能倒,A要留剩下,如果没满,就相当于把A清空了。这是要注意的地方。
第二步:特殊处理
- if(aim == )
- {
- printf("success\n");
- continue;
- }
- if(temp_b == aim)
- {
- printf("fill B\n");
- printf("success\n");
- continue;
- }
- if(temp_a == aim)
- {
- printf("fill A\n");
- printf("pour A B\n");
- printf("success\n");
- continue;
- }
这个就是我超时的原因之一了,因为我没有考虑到 3 5 3 或者是 3 5 5这种情况,当目标数直接等于其中一个杯子量时的操作,就会让程序一直循环操作得不到结果超时了。各位要注意。
第三步:进行循环了
- if(temp_a >= aim)
- {
- if(cap_a == ) fill(true);
- pour(true);
- if(cap_b == temp_b) empty(true);
- }
- else
- {
- if(cap_b == ) fill(false);
- pour(false);
- if(cap_a == temp_a && cap_b != aim) empty(false);
- }
这里就要提到我刚刚分析的时候说的最短步骤问题了,如果目标数C小于A,必须从A开始倒满。如果目标数C大于A,则必须从B开始倒满。就在这里体现,核心步骤其实很简单,就是刚刚的三步,填满、移动、清空已满。
第四步:得到结果退出永真循环
- if(cap_a == aim)
- {
- if(cap_b != ) printf("empty B\n");
- printf("pour A B\n");
- printf("success\n");
- break;
- }
- if(cap_b == aim)
- {
- printf("success\n");
- break;
- }
(四) AC代码:
- #include<bits/stdc++.h>
- using namespace std;
- int temp_a,temp_b,aim;
- int cap_a,cap_b;
- void pour(bool x)
- {
- if(x)
- {
- if(cap_b + cap_a <= temp_b)
- {
- cap_b = cap_b + cap_a;
- cap_a = ;
- }
- else
- {
- cap_a = cap_a - (temp_b - cap_b);
- cap_b = temp_b;
- }
- printf("pour A B\n");
- }
- else
- {
- if(cap_b + cap_a <= temp_a)
- {
- cap_a = cap_b + cap_a;
- cap_b = ;
- }
- else
- {
- cap_b = cap_b - (temp_a - cap_a);
- cap_a = temp_a;
- }
- printf("pour B A\n");
- }
- }
- void fill(bool x)
- {
- if(x)
- {
- cap_a = temp_a;
- printf("fill A\n");
- }
- else
- {
- cap_b = temp_b;
- printf("fill B\n");
- }
- }
- void empty(bool x)
- {
- if(x)
- {
- cap_b = ;
- printf("empty B\n");
- }
- else
- {
- cap_a = ;
- printf("empty A\n");
- }
- }
- int main()
- {
- while(~scanf("%d %d %d",&temp_a,&temp_b,&aim))
- {
- if(aim == )
- {
- printf("success\n");
- continue;
- }
- if(temp_b == aim)
- {
- printf("fill B\n");
- printf("success\n");
- continue;
- }
- if(temp_a == aim)
- {
- printf("fill A\n");
- printf("pour A B\n");
- printf("success\n");
- continue;
- }
- cap_a = cap_b = ;//记得每个样例要清空杯子
- for(;;)
- {
- if(temp_a >= aim)
- {
- if(cap_a == ) fill(true);
- pour(true);
- if(cap_b == temp_b) empty(true);
- }
- else
- {
- if(cap_b == ) fill(false);
- pour(false);
- if(cap_a == temp_a && cap_b != aim) empty(false);
- }
- if(cap_a == aim)
- {
- if(cap_b != ) printf("empty B\n");
- printf("pour A B\n");
- printf("success\n");
- break;
- }
- if(cap_b == aim)
- {
- printf("success\n");
- break;
- }
- }
- }
- return ;
- }
(五)AC截图:
(六) 解后分析:
这道题如果不要求最短路径的话,其实就是非常简单的题目了,只要循环那三个步骤肯定能出结果,而且代码量直接大大减少。不过这道题解法我是比较直接的一种解法,在解后去网上找找别的解法,那就是还有一种就是用BFS(宽度优先搜索)
代码贴上:
- #include <cstdio>
- #include <algorithm>
- #include <cstring>
- #include <queue>
- #include <vector>
- using namespace std;
- const int MAXN=;
- struct node{
- int a,b;
- node(int _a,int _b):a(_a),b(_b){}
- };
- int A,B,N;
- int vis[MAXN][MAXN];
- vector<int> path[MAXN][MAXN];
- void op(int &a,int &b,int i){
- switch(i){
- case :{
- a=A;
- break;
- }
- case :{
- b=B;
- break;
- }
- case :{
- a=;
- break;
- }
- case :{
- b=;
- break;
- }
- case :{
- if(a+b>B){
- a=(a+b)-B;
- b=B;
- }
- else{
- b+=a;
- a=;
- }
- break;
- }
- case :{
- if(b+a>A){
- b=(b+a)-A;
- a=A;
- }
- else{
- a+=b;
- b=;
- }
- break;
- }
- }
- }
- void op_print(int i){
- switch(i){
- case :{
- printf("fill A\n");
- break;
- }
- case :{
- printf("fill B\n");
- break;
- }
- case :{
- printf("empty A\n");
- break;
- }
- case :{
- printf("empty B\n");
- break;
- }
- case :{
- printf("pour A B\n");
- break;
- }
- case :{
- printf("pour B A\n");
- break;
- }
- }
- }
- void bfs(){
- memset(vis,-,sizeof(vis));
- for(int i=;i<A;i++){
- for(int j=;j<B;j++){
- path[i][j].clear();
- }
- }
- queue<node> que;
- que.push(node(,));
- vis[][]=;
- while(!que.empty()){
- node tmp=que.front();
- que.pop();
- int ta=tmp.a;
- int tb=tmp.b;
- if(tb==N){
- for(int i=;i<path[ta][tb].size();i++){
- op_print(path[ta][tb][i]);
- }
- printf("success\n");
- return;
- }
- for(int i=;i<=;i++){
- int ta=tmp.a;
- int tb=tmp.b;
- op(ta,tb,i);
- if(vis[ta][tb]==-){
- vis[ta][tb]=vis[tmp.a][tmp.b]+;
- for(int j=;j<vis[tmp.a][tmp.b];j++){
- path[ta][tb].push_back(path[tmp.a][tmp.b][j]);
- }
- path[ta][tb].push_back(i);
- que.push(node(ta,tb));
- }
- }
- }
- }
- int main(void){
- while(~scanf("%d%d%d",&A,&B,&N)){
- bfs();
- }
- return ;
- }
参考:https://blog.csdn.net/westbrook1998/article/details/80937164
好了由于时间关系,先写在这,因为要睡觉了hhhh,明天满课,从早八点到晚九点半,要死,所以还是先早睡啦~
(1)大数计算:加减乘除乘方开根问题
(2)BFS搜索算法了解
(3)灌水定理研究
这是这周的任务了吧,这周解决这三个点!!~~
注:如果有更好的解法,真心希望您能够评论留言贴上您的代码呢~互相帮助互相鼓励才能成长鸭~~
『ACM C++』HDU杭电OJ | 1415 - Jugs (灌水定理引申)的更多相关文章
- 『ACM C++』HDU杭电OJ | 1418 - 抱歉 (拓扑学:多面体欧拉定理引申)
呕,大一下学期的第一周结束啦,一周过的挺快也挺多出乎意料的事情的~ 随之而来各种各样的任务也来了,嘛毕竟是大学嘛,有点上进心的人多多少少都会接到不少任务的,忙也正常啦~端正心态 开心面对就好啦~ 今天 ...
- 『ACM C++』HDU杭电OJ | 1425 - sort (排序函数的特殊应用)
今天真的是累哭了,周一课从早八点半一直上到晚九点半,整个人要虚脱的感觉,因为时间不太够鸭所以就回头看看找了一些比较有知识点的题来总结总结分析一下,明天有空了就开始继续打题,嘻嘻嘻. 今日兴趣电影: & ...
- 『ACM C++』HDU杭电OJ | 1416 - Gizilch (DFS - 深度优先搜索入门)
从周三课开始总算轻松了点,下午能在宿舍研究点题目啥的打一打,还好,刚开学的课程还算跟得上,刚开学的这些课程也是复习以前学过的知识,下半学期也不敢太划水了,被各种人寄予厚望之后瑟瑟发抖,只能努力前行了~ ...
- C#利用POST实现杭电oj的AC自动机器人,AC率高达50%~~
暑假集训虽然很快乐,偶尔也会比较枯燥,,这个时候就需要自娱自乐... 然后看hdu的排行榜发现,除了一些是虚拟测评机的账号以外,有几个都是AC自动机器人 然后发现有一位作者是用网页填表然后按钮模拟,, ...
- 用python爬取杭电oj的数据
暑假集训主要是在杭电oj上面刷题,白天与算法作斗争,晚上望干点自己喜欢的事情! 首先,确定要爬取哪些数据: 如上图所示,题目ID,名称,accepted,submissions,都很有用. 查看源代码 ...
- 爬取杭电oj所有题目
杭电oj并没有反爬 所以直接爬就好了 直接贴源码(参数可改,循环次数可改,存储路径可改) import requests from bs4 import BeautifulSoup import ti ...
- 杭电oj 2095 & 异或^符号在C/C++中的使用
异或^符号,在平时的学习时可能遇到的不多,不过有时使用得当可以发挥意想不到的结果. 值得注意的是,异或运算是建立在二进制基础上的,所有运算过程都是按位异或(即相同为0,不同为1,也称模二加),得到最终 ...
- 杭电oj 4004---The Frog Games java解法
import java.util.Arrays; import java.util.Scanner; //杭电oj 4004 //解题思路:利用二分法查找,即先选取跳跃距离的区间,从最大到最小, // ...
- 杭电oj————2057(java)
question:A+ B again 思路:额,没啥思路/捂脸,用java的long包里的方法,很简单,只是有几次WA,有几点要注意一下 注意:如果数字有加号要删除掉,这里用到了正则表达式“\\+” ...
随机推荐
- 深入理解 Java 内存模型(转载)
摘要: 原创出处 http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/ 「zhisheng」欢迎转载,保留摘要,谢谢! 0. 前提 &l ...
- 《本博客将搬至CSDN》 博客主QQ 654436731 有关于本博客任何文章的问题欢迎打扰
地址 http://blog.csdn.net/sajiazaici
- shutil模块——高级的文件、文件夹、压缩包处理模块
将文件内容拷贝到另一个文件 shutil.copyfileobj('fsrc', 'fdst', 'length') 方法源码: def copyfileobj(fsrc, fdst, length= ...
- Vue表格中,对数据进行转换、处理
众所周知,后端从Mysql取出的数据,一般是很难单独处理某一个Key的数据的(需要处理的话,可能会浪费大量的性能.而且对页面加载时间有很大的影响),所以,从数据库取出的数据.只能由前端进行处理.但是在 ...
- HBase伪分布式安装(HDFS)+ZooKeeper安装+HBase数据操作+HBase架构体系
HBase1.2.2伪分布式安装(HDFS)+ZooKeeper-3.4.8安装配置+HBase表和数据操作+HBase的架构体系+单例安装,记录了在Ubuntu下对HBase1.2.2的实践操作,H ...
- 【Linux】Core dump故障分析
引入: Q:如果一个程序运行3天后才会出错,这个时候难道需要我们一直用GDB调试程序3天吗? A:答案当然是否定的. 我们有更厉害的工具--Core dump 一.Coredump定义 Core Du ...
- jetbrain rider 逐渐完美了,微软要哭了么?
2019-03-24 10:08:42 多年的vsiual studio使用经验,各种小瑕疵:到现在的visual studio是越来越大了:简直到了无法忍受境地: 每次重装系统都要重新安装下,这个不 ...
- 一键生成http服务器
如果你想用最简单的方法在内网共享目录,可以考虑为要共享的目录生成一个http服务器,这样就可以在内网任一台设备打开浏览器就可以浏览了.简单举几个例,有了这个http服务器就可以: 在手机浏览器里观看电 ...
- ZT C++关键字new学习
http://blog.csdn.net/waken_ma/article/details/4007914 C++关键字new学习 很多新手对C++关键字new可能不是很了解吧,今天我一起来学习一下. ...
- Python3基本数据类型(一、数字类型)
第一次写博客,感觉心情比较紧张,有一种要上台演讲的紧张感(虽然可能大概也许不会有人看).在此立个flag,以后每个学习阶段都要写一篇博客,来记录自己学习成长的这段日子.Fighting! 废话不多说, ...