浙大ZOJ 1005 Jugs问题解决

来源:互联网 发布:天刀明月心官方数据 编辑:程序博客网 时间:2024/04/29 22:29

今天提交的时候有点小郁闷,第一次提交把编译器选错了(选了GCC),太大意了!

一、工程代码及算法设计注释

--------------------------------------------------jugs.h----------------------------------------------

#ifndef JLU_CCST_GDC_JUGS_H#define JLU_CCST_GDC_JUGS_Hextern bool findJugsSolution(int nA,int nB,int cA,int cB,int N);extern void testFindJugsSolution();#endif//JLU_CCST_GDC_JUGS_H

--------------------------------------------------jugs.cpp----------------------------------------------

/**题目来源:浙大ACM在线测试——ZOJ,题目编号1005,题名"Jugs"URL:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1005Author:hellogdc<gdcjlu@163.com>Finish Time:2014.01.03*//**算法设计:利用递归思想:求解某一个状态下的可解序列可以通过以下几个动作,将问题转化为求解子问题的可解序列。某一个状态(nA,nB,cA,cB,N),其中nA、nB分别表示在A罐和B罐中水的量,cA、cB分别表示A罐和B罐中的容量,N为B罐需要达到的目标,可以做的动作如下:1. Fill A。可以采取该动作的条件是:nA<cA。2. Empty A。可以采取该动作的条件是:nA>0.3. Pour B A。可以采取该动作的条件是:nB>0&&nA<cA。4. Fill B。可以采取该动作的条件是:nB<cB。5. Empty B。可以采取该动作的条件是:nB>0。6. Pour A B。可以采取该动作的条件是:nA>0&&nB<cB。递归出口:nB==N || cB<N问题:这个递归会一直执行,直到找到一个可解序列。因为是穷举法,所以如果存在解,那么终会结束。问题是,如果无解,那么该递归将会一直执行下去。怎么办呢?需要设置一个递归深度什么的吗?*/#include <iostream>#include <iomanip>#include <string>#include <list>#include <set>using namespace std;namespace acm_gdc{const int ACTIONS_COUNT=6;string actions[ACTIONS_COUNT]={"Fill A","Empty A","Fill B","Empty B","Pour A B","Pour B A"};list<string> solution;struct Status{//findJugsSolution2(),findJugsSolution3(),findJugsSolution4()int x,y;Status(int xx,int yy):x(xx),y(yy) {};bool operator==(const Status& s1){return (x==s1.x&&y==s1.y);}string toString(){char str[20];sprintf(str,"(%d,%d)",x,y);return string(str);}};int prevOp=4;//记录上一步的操作list<Status> listForStack;//findJugsSolution3(),findJugsSolution4()/**提高性能:1. 上述算法虽可以执行,但仔细一分析,就会发现它做了很多无用功。比如Fill和Empth反复间隔执行。为了去掉无用动作,提高性能,对上述回溯做如下剪枝处理:1.1 Fill A和EmpthA不能连续执行。1.2 Fill B和EmpthB不能连续执行。1.3 Pour B A和Pour A B不能连续执行。为此,需要一个辅助变量,记录上一步的操作。2. 一执行,发现还是执行太长时间。仔细一分析,发现还是存在一些无用动作:2.1 FillA后能执行的动作只有Pour A B。2.2 FillB后能执行的动作只有Pour B A。2.3 EmptyA后能执行的动作只有Pour B A。2.4 EmptyB后能执行的动作只有Pour A B。*/const int DEPTH_MAX=50;bool findJugsSolution1(int nA,int nB,int cA,int cB,int N,int depth,int maxDepth){//出口if(depth>maxDepth||cB<N)return false;if(nB==N){for(list<string>::iterator it=solution.begin();it!=solution.end();it++){cout<<(*it)<<endl;}cout<<"success"<<endl;return true;}//执行子动作for(int i=0;i<ACTIONS_COUNT;i++){//可以执行0,1,2,3步if(prevOp==4||prevOp==5){switch(i){case 0://Fill A//if(nA>=cA||prevOp==1)if(nA>=cA)continue;nA=cA;break;case 1://Empty A//if(nA<=0||prevOp==0)if(nA<=0)continue;nA=0;break;case 2://Fill B//if(nB>=cB||prevOp==4)if(nB>=cB)continue;nB=cB;break;case 3://Empty B//if(nB<=0||prevOp==3)if(nB<=0)continue;nB=0;break;default:continue;}}else{switch(i){case 5://Pour B Aif(nB<=0||nA>=cA)continue;if(nB>(cA-nA)){nB-=(cA-nA);nA=cA;}else{nA+=nB;nB=0;}break;case 4://Pour A Bif(nA<=0||nB>=cB)continue;if(nA>(cB-nB)){nA-=(cB-nB);nB=cB;}else{nB+=nA;nA=0;}break;default:continue;}}prevOp=i;//将动作压栈solution.push_back(actions[i]);if( findJugsSolution1(nA,nB,cA,cB,N,depth+1,maxDepth) )return true;//恢复栈solution.pop_back();}return false;}/**不行,性能还是不行,还发现一个剪枝的办法,那就是,一个状态只能出现一次,如果重复出现,那么将下面的分支剪掉。这样就需要一个保存状态的列表,元素最多为cA*cB。但是结果却是错误的,蛋疼!估计是由于我采用了findJugsSolution()中的那些繁杂的剪枝方法。这样,如果能结合最初始的回溯方法,再结合这里的状态检查,这样便能保证在cA*cB次递归内结束递归,找到相应的解。*/struct StatusComparator{bool operator()(const Status& s1,const Status& s2){if(s1.x<s2.x)return true;if( s1.x==s2.x && s1.y<s2.y )return true;return false;}};set<Status,StatusComparator> statusSet;bool findJugsSolution2(int nA,int nB,int cA,int cB,int N){//出口if(cB<N)return false;if(nB==N){for(list<string>::iterator it=solution.begin();it!=solution.end();it++){cout<<(*it)<<endl;}cout<<"success"<<endl;return true;}//检查该状态是否已经出现Status st(nA,nB);if(statusSet.find(st)!=statusSet.end())return false;statusSet.insert(st);//执行子动作for(int i=0;i<ACTIONS_COUNT;i++){switch(i){case 0://Fill Aif(nA>=cA)continue;nA=cA;break;case 1://Empty Aif(nA<=0)continue;nA=0;break;case 2://Fill Bif(nB>=cB)continue;nB=cB;break;case 3://Empty Bif(nB<=0)continue;nB=0;break;case 4://Pour A Bif(nA<=0||nB>=cB)continue;if(nA>(cB-nB)){nA-=(cB-nB);nB=cB;}else{nB+=nA;nA=0;}break;case 5://Pour B Aif(nB<=0||nA>=cA)continue;if(nB>(cA-nA)){nB-=(cA-nA);nA=cA;}else{nA+=nB;nB=0;}break;default:continue;}//将动作压栈solution.push_back(actions[i]);if( findJugsSolution2(nA,nB,cA,cB,N) )return true;//恢复栈solution.pop_back();}return false;}/**经过执行,发现findJugsSolution2()中关于状态检测的分析是错误的,因为不同分支里状态有可能会出现重复。那可不可以这样:每个分支路有一个状态表来监测该分支路是否可以继续下去。这样就出现一个问题:如何给每个分支路维护这么一个状态表?解决方法就是栈。*/bool findJugsSolution3(int nA,int nB,int cA,int cB,int N){//出口if(cB<N)return false;if(nB==N){for(list<string>::iterator it=solution.begin();it!=solution.end();it++){cout<<(*it)<<endl;}cout<<"success"<<endl;return true;}//检查该状态是否已经出现Status st(nA,nB);for(list<Status>::iterator it=listForStack.begin();it!=listForStack.end();it++){if((*it)==st)return false;}listForStack.push_front(st);//执行子动作bool isFound=false;int nABack=nA,nBBack=nB;for(int i=0;i<ACTIONS_COUNT&&(!isFound);i++){switch(i){case 0://Fill Aif(nA>=cA)continue;nA=cA;break;case 1://Empty Aif(nA<=0)continue;nA=0;break;case 2://Fill Bif(nB>=cB)continue;nB=cB;break;case 3://Empty Bif(nB<=0)continue;nB=0;break;case 4://Pour A Bif(nA<=0||nB>=cB)continue;if(nA>(cB-nB)){nA-=(cB-nB);nB=cB;}else{nB+=nA;nA=0;}break;case 5://Pour B Aif(nB<=0||nA>=cA)continue;if(nB>(cA-nA)){nB-=(cA-nA);nA=cA;}else{nA+=nB;nB=0;}break;default:continue;}//将动作压栈solution.push_back(actions[i]);if( findJugsSolution3(nA,nB,cA,cB,N) ){isFound=true;}//恢复栈solution.pop_back();//回复nA和nBnA=nABack;nB=nBBack;}//恢复栈listForStack.pop_front();return isFound;}/**findJugsSolution3()虽然已经可以得到正确的结果,但是仍然会出现一些冗余步骤。所以再加上如下规定:即参考findJugsSolution()中的分析2.1 FillA后能执行的动作只有Pour A B。2.2 FillB后能执行的动作只有Pour B A。2.3 EmptyA后能执行的动作只有Pour B A。2.4 EmptyB后能执行的动作只有Pour A B。*/bool findJugsSolution4(int nA,int nB,int cA,int cB,int N){//出口if(cB<N)return false;//检查该状态是否已经出现Status st(nA,nB);for(list<Status>::iterator it=listForStack.begin();it!=listForStack.end();it++){if((*it)==st)return false;}listForStack.push_back(st);if(nB==N){list<Status>::iterator sIt=listForStack.begin();sIt++;//状态(0,0)就不输出了for(list<string>::iterator it=solution.begin();it!=solution.end();it++){cout<<setw(actions[ACTIONS_COUNT-1].length())<<(*it)<<'\t'<<(sIt->toString())<<endl;sIt++;}cout<<"success"<<endl;return true;}//执行子动作bool isFound=false;int nABack=nA,nBBack=nB,prevOpBack=prevOp;for(int i=0;i<ACTIONS_COUNT&&(!isFound);i++){//可以执行0,1,2,3步if(prevOp==4||prevOp==5){switch(i){case 0://Fill Aif(nA>=cA)continue;nA=cA;break;case 1://Empty Aif(nA<=0)continue;nA=0;break;case 2://Fill Bif(nB>=cB)continue;nB=cB;break;case 3://Empty Bif(nB<=0)continue;nB=0;break;default:continue;}}else{switch(i){case 5://Pour B Aif(nB<=0||nA>=cA)continue;if(nB>(cA-nA)){nB-=(cA-nA);nA=cA;}else{nA+=nB;nB=0;}break;case 4://Pour A Bif(nA<=0||nB>=cB)continue;if(nA>(cB-nB)){nA-=(cB-nB);nB=cB;}else{nB+=nA;nA=0;}break;default:continue;}}prevOp=i;//将动作压栈solution.push_back(actions[i]);if( findJugsSolution4(nA,nB,cA,cB,N) ){isFound=true;}//恢复栈solution.pop_back();//恢复nA、nB和prevOpnA=nABack;nB=nBBack;prevOp=prevOpBack;}//恢复栈listForStack.pop_back();return isFound;}void testFindJugsSolution1(){int nA=0,nB=0,cA=3,cB=5,N=4;bool isFound=findJugsSolution1(nA,nB,cA,cB,N,0,DEPTH_MAX);if(!isFound)cout<<"No solution"<<endl;cout<<"End"<<endl;}void testFindJugsSolution2(){int nA=0,nB=0,cA=3,cB=5,N=4;bool isFound=findJugsSolution2(nA,nB,cA,cB,N);if(!isFound)cout<<"No solution"<<endl;cout<<"**************************************************************"<<endl;nA=nB=0;cA=5;cB=7;N=3;isFound=findJugsSolution2(nA,nB,cA,cB,N);if(!isFound)cout<<"No solution"<<endl;cout<<"**************************************************************"<<endl;}void testFindJugsSolution3(){int nA=0,nB=0,cA=3,cB=5,N=4;bool isFound=findJugsSolution3(nA,nB,cA,cB,N);if(!isFound)cout<<"No solution"<<endl;cout<<"**************************************************************"<<endl;nA=nB=0;cA=5;cB=7;N=3;isFound=findJugsSolution3(nA,nB,cA,cB,N);if(!isFound)cout<<"No solution"<<endl;cout<<"**************************************************************"<<endl;}void testFindJugsSolution4(){int nA=0,nB=0,cA=3,cB=5,N=4;bool isFound=true;prevOp=4;//这么做,是为了让第1步为actions中的前4个动作solution.clear();listForStack.clear();isFound=findJugsSolution4(nA,nB,cA,cB,N);if(!isFound)cout<<"No solution"<<endl;cout<<"**************************************************************"<<endl;nA=nB=0;cA=5;cB=7;N=3;prevOp=4;//这么做,是为了让第1步为actions中的前4个动作solution.clear();listForStack.clear();isFound=findJugsSolution4(nA,nB,cA,cB,N);if(!isFound)cout<<"No solution"<<endl;cout<<"**************************************************************"<<endl;}void testAll(){/**经测试:findJugsSolution1()既慢又得不到正确结果findJugsSolution2够快,但是得不到正确结果findJugsSolution3()较慢,但可以得到正确结果,虽然结果中包含很多冗余的步骤findJugsSolution4则既可以较快的得到正确结果,也将部分冗余步骤删掉了*/testFindJugsSolution1();testFindJugsSolution2();testFindJugsSolution3();testFindJugsSolution4();}}bool findJugsSolution(int nA,int nB,int cA,int cB,int N){return acm_gdc::findJugsSolution4(nA,nB,cA,cB,N);}void testFindJugsSolution(){acm_gdc::testFindJugsSolution4();}

--------------------------------------------------main.cpp----------------------------------------------

#if 1#include "jugs.h"int main(){testFindJugsSolution();}#endif

二、提交并被ZOJ成功接受的代码——算法核心代码

--------------------------------------------------submit_main.cpp----------------------------------------------

/**all code are copied from jugs.cpp*/#include <iostream>#include <iomanip>#include <string>#include <list>#include <set>using namespace std;const int ACTIONS_COUNT=6;string actions[ACTIONS_COUNT]={"fill A","empty A","fill B","empty B","pour A B","pour B A"};list<string> solution;struct Status{int x,y;Status(int xx,int yy):x(xx),y(yy) {};bool operator==(const Status& s1){return (x==s1.x&&y==s1.y);}};int prevOp=4;list<Status> listForStack;bool findJugsSolutionSubmit(int nA,int nB,int cA,int cB,int N){if(cB<N)return false;Status st(nA,nB);for(list<Status>::iterator it=listForStack.begin();it!=listForStack.end();it++){if((*it)==st)return false;}listForStack.push_back(st);if(nB==N){for(list<string>::iterator it=solution.begin();it!=solution.end();it++){cout<<(*it)<<endl;}cout<<"success"<<endl;return true;}bool isFound=false;int nABack=nA,nBBack=nB,prevOpBack=prevOp;for(int i=0;i<ACTIONS_COUNT&&(!isFound);i++){if(prevOp==4||prevOp==5){switch(i){case 0://Fill Aif(nA>=cA)continue;nA=cA;break;case 1://Empty Aif(nA<=0)continue;nA=0;break;case 2://Fill Bif(nB>=cB)continue;nB=cB;break;case 3://Empty Bif(nB<=0)continue;nB=0;break;default:continue;}}else{switch(i){case 5://Pour B Aif(nB<=0||nA>=cA)continue;if(nB>(cA-nA)){nB-=(cA-nA);nA=cA;}else{nA+=nB;nB=0;}break;case 4://Pour A Bif(nA<=0||nB>=cB)continue;if(nA>(cB-nB)){nA-=(cB-nB);nB=cB;}else{nB+=nA;nA=0;}break;default:continue;}}prevOp=i;solution.push_back(actions[i]);if( findJugsSolutionSubmit(nA,nB,cA,cB,N) ){isFound=true;}solution.pop_back();nA=nABack;nB=nBBack;prevOp=prevOpBack;}listForStack.pop_back();return isFound;}int main(){int A,B,target;while(cin>>A>>B>>target){prevOp=4;solution.clear();listForStack.clear();findJugsSolutionSubmit(0,0,A,B,target);}return 0;}


0 0
原创粉丝点击