【P1432 倒水问题】题解

· · 题解

update:修正了代码中的一处错误,感谢指正

这道题被我们用来团队考试了,这是我唯一一道ac的题,写个题解纪念一下。

首先拿道题,我就想到了广搜,因为最短路径的题一般都是广搜,且这道题的状态在可接受的范围之内,事实证明我也是对的,思路很简单:设置两个状态为a,b表示当前状态a杯和b杯的水量,每次从队列中读到一个状态之后,将这个状态执行题目中所给的六种操作,并将执行了六种操作之后的状态压入队列,记录答案并且继续搜索。

说完了整体思路,接下来就是本题的难点(易错点)了,首先第一条,如何解决掉操作5和操作6 (可能不是很难,但是我在考场上搞了半天没搞对),首先读题干,题干告诉我们从B向A倒水会有两种情况:
1、直到A满
2、直到B没水
那么目标就很明确了,若是从B向A倒水,比较B的水量和A杯空着的体积做比较,若大于,则A=A满,B-=A的剩余体积,否则则是A+=B,B=0。
反过来倒一样。
然后就是记录解的问题,我这里使用的是vector存解,每次将状态压进队列的时候顺带将这个状态所经历的步骤也压进去,同时vector的大小便是这个状态的步数,这里看不懂的可以直接去看代码。
接下来该思考如何剪枝了,首先我们不难发现我们在搜索中会遇到很多重复的状态,但是根据广搜的性质,我们后搜到的状态一定没有之前的搜索更优,所以我们将以搜到的情况进行标记,防止出现重复的无意义搜索。
然后就是一些细枝末节的剪枝,比如当杯子已经是空的就不要empty了,已经满的就不要fill了,这都很好理解。
ac代码注释版奉上。

#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
int ca,cb,n;//ca和cb分别表示两个杯子的最大容量,n表示目标值。
bool rem[1005][1005];
struct node
{
    int x,y;//x和y代表着a杯和b杯当前的水量
    vector  <int> ans;
};
void bfs(int x,int y){
    memset(rem,0,sizeof(rem));//不要忘了重置标记数组!!!!
    queue  <node> q;//搜索队列
    node ppp;
    ppp.x=x;
    ppp.y=y;
    q.push(ppp);
    while(1){
        node temp;
        temp=q.front();//从队头取出一个搜索状态
        q.pop();
        ///////////////////////////////////////
        if(temp.y==n){//终止条件
            cout<<temp.ans.size()<<" ";//输出步数
            for(int i=0;i<temp.ans.size();i++){
                cout<<temp.ans[i]<<" ";//输出解
            }
            cout<<endl;
            break;
        }
        ///////////////////////////////////////
        if(temp.x!=ca){//处理方式1
            node kater=temp;
            kater.x=ca;//这里由于我ca太菜不会构造函数,所以牺牲了美观性QAQ,别打我。
            kater.ans.push_back(1);//将新构筑出来的状态的操作后面加上个“1”。后面同理。
            if(!rem[kater.x][kater.y]){//如果之前被搜索过就不把他放到队列里
                q.push(kater);
                rem[kater.x][kater.y]=1;//记忆化,防止重搜
            }
        }
        ///////////////////////////////////////
        if(temp.y!=cb){//处理方式2
            node kater=temp;
            kater.y=cb;
            kater.ans.push_back(2);
            if(!rem[kater.x][kater.y]){
                q.push(kater);
                rem[kater.x][kater.y]=1;
            }
        }
        ///////////////////////////////////////
        if(temp.x!=0){//处理方式3
            node kater=temp;
            kater.x=0;
            kater.ans.push_back(3);
            if(!rem[kater.x][kater.y]){
                q.push(kater);
                rem[kater.x][kater.y]=1;
            }
        }
        ///////////////////////////////////////
        if(temp.y!=0){//处理方式4
            node kater=temp;
            kater.y=0;
            kater.ans.push_back(4);
            if(!rem[kater.x][kater.y]){
                q.push(kater);
                rem[kater.x][kater.y]=1;
            }
        }
        ///////////////////////////////////////
        if(temp.y!=0&&temp.x!=ca){//处理方式5
            node kater=temp;
            if(kater.y>ca-kater.x){
                kater.y-=ca-kater.x;
                kater.x=ca;
            }
            else{
                kater.x+=kater.y;
                kater.y=0;
            }//模拟倒水,注意这里的顺序不能反。
            kater.ans.push_back(5);
            if(!rem[kater.x][kater.y]){
                q.push(kater);
                rem[kater.x][kater.y]=1;
            }
        }
        ///////////////////////////////////////
        if(temp.x!=0&&temp.y!=cb){//处理方式6
            node kater=temp;
            if(kater.x>cb-kater.y){
                kater.x-=cb-kater.y;
                kater.y=cb;
            }
            else{
                kater.y+=kater.x;
                kater.x=0;
            }
            kater.ans.push_back(6);
            if(!rem[kater.x][kater.y]){
                q.push(kater);
                rem[kater.x][kater.y]=1;
            }
        }
    }
}
int main(){
    //    freopen("test.in","r",stdin);
    int T;
    cin>>T;
    while(T--){
        cin>>ca>>cb>>n;
        bfs(0,0);
    }
    return 0;
}

求过QAQ,审核员大大最帅了。