非常可乐(BFS和最短路)

来源:互联网 发布:网红穆雅斓的淘宝店铺 编辑:程序博客网 时间:2024/05/17 03:00

原题链接
非常可乐
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Submit

Status
Description
大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出”NO”。
Input
三个整数 : S 可乐的体积 , N 和 M是两个杯子的容量,以”0 0 0”结束。
Output
如果能平分的话请输出最少要倒的次数,否则输出”NO”。
Sample Input
7 4 3
4 1 3
0 0 0
Sample Output
NO
3

问题很简单,其实就是模拟倒可乐的过程,但是每次倒都有多种选择,而且又让求出最少倒多少次,所以只能用BFS。总结一下

同时,BFS其实就是每步的代价为1的最短路问题,最短路每次到达一个状态后,都会更新下这个状态下的最大值,并且如果能够更新那么他的下一步也会加入队列中去,否则不会加入到队列中。同时对于每个状态除了判断是否走过的vis数组,还会有一个初始值是-1的in数组用来统计是否有最短路和最短路是多大。最后不断地对队列进行循环,对于BFS,如果中间有解就直接输出了,那就是最优解,如果是最短路那么只需要更新到这个状态的最短路径即可,最后循环完后输出无解或者是有最优解即可。SPFA和BFS的区别就在于BFS只进一次队,SPFA(最短路)进若干次队

#include <cstdio>#include <cstring>#include <iostream>#include <queue>#include <algorithm>using namespace std;const int maxn=102;typedef struct node{        int a[3];}node;node c;//这里的c代表三个瓶子的容量上限int in[maxn][maxn][maxn];//初始值为-1,当存有次数的统计值时,就不是-1了bool vis[maxn][maxn][maxn];bool cmp(int x,int y){        return x>y;}//检查now.a[i]向now.a[j]中倒可乐是否可行,同时如果可以就修改newx中的值bool can(node now,node& newx,int i,int j){        int shengyu = c.a[j] - now.a[j];        if(shengyu <= 0)                return false;        for(int k=0;k<3;k++)                if(k!=i&&k!=j) newx.a[k]=now.a[k];        if(shengyu > now.a[i]){                newx.a[i]=0;                newx.a[j]=now.a[i]+now.a[j];        }        else{                newx.a[i]=now.a[i]-shengyu;                newx.a[j]=c.a[j];        }        if(vis[newx.a[0]][newx.a[1]][newx.a[2]]) return false;        else return true;}void BFS(node t){        memset(in,-1,sizeof(in));        memset(vis,false,sizeof(vis));//false说明可以访问        queue<node> q;        q.push(t);        vis[t.a[0]][t.a[1]][t.a[2]]=true;        in[t.a[0]][t.a[1]][t.a[2]]=0;        while(!q.empty()){                node now=q.front();                q.pop();                for(int i=0;i<3;i++){                        for(int j=0;j<3;j++){                                if(i!=j){                                        node newx;                                        if(can(now,newx,i,j)){                                                //q.push(newx);就算算了之后是可以倒过去的,但是现在仍然不能入队                                                int res = in[now.a[0]][now.a[1]][now.a[2]] + 1;//BFS就是每一步代价都是1的最短路,这里日后还可以改成最短路的                                                if(in[newx.a[0]][newx.a[1]][newx.a[2]] == -1 || res < in[newx.a[0]][newx.a[1]][newx.a[2]]){                                                        in[newx.a[0]][newx.a[1]][newx.a[2]] = res;                                                        if(!vis[newx.a[0]][newx.a[1]][newx.a[2]]){                                                                q.push(newx);                                                                vis[newx.a[0]][newx.a[1]][newx.a[2]]=true;                                                        }                                                }                                        }                                }                        }                }        }        //分配完成的情况就是最大的两个瓶子里把所有的可乐都平分了        if(c.a[1]>=c.a[2]){                int mid=c.a[0]/2;                if(in[mid][mid][0]==-1) cout << "NO" <<endl;                else cout << in[mid][mid][0] << endl;        }        else{                int mid=c.a[0]/2;                if(in[mid][0][mid]==-1) cout << "NO" <<endl;                else cout << in[mid][0][mid] << endl;        }}int main(){        while(scanf("%d%d%d",&c.a[0],&c.a[1],&c.a[2])==3 && !(c.a[0]==0 && c.a[1]==0 && c.a[2]==0)){                if(c.a[0] % 2 == 1) cout << "NO" << endl;                else{                        node t;                        t.a[0]=c.a[0]; t.a[1]=0; t.a[2]=0;//将饮料的数量初始化                        BFS(t);                }        }        return 0;}

上面这个代码是通过的,但是下面这个代码不行,虽然他的对于下标的运算要更快,但是已经开的空间实在是太大了,而本题由于边最大到100,所以也不能用*1,*100,*10000来解决。但是对于范围小的题来说,这样的下标运算是要快很多的
下面给出超空间的代码

#include <cstdio>#include <cstring>#include <iostream>#include <queue>#include <algorithm>using namespace std;typedef struct node{        int a[3];}node;node c;//这里的c代表三个瓶子的容量上限int in[20000000];//初始值为-1,当存有次数的统计值时,就不是-1了bool vis[20000000];//假设三个杯子的从大到小的顺序是a[0],a[1],a[2],那么下标为a[0] + (a[1]<<7)  + (a[2]<<14);bool cmp(int x,int y){        return x>y;}//检查now.a[i]向now.a[j]中倒可乐是否可行,同时如果可以就修改newx中的值bool can(node now,node& newx,int i,int j){        int shengyu = c.a[j] - now.a[j];        if(shengyu <= 0)                return false;        for(int k=0;k<3;k++)                if(k!=i&&k!=j) newx.a[k]=now.a[k];        if(shengyu > now.a[i]){                newx.a[i]=0;                newx.a[j]=now.a[i]+now.a[j];        }        else{                newx.a[i]=now.a[i]-shengyu;                newx.a[j]=c.a[j];        }        if(vis[newx.a[0] + (newx.a[1]<<7) + (newx.a[2]<<14)]) return false;        //注意这里所有的位运算都要加括号括起来,否则会出现把后面的数也算进去的结果        else return true;}void BFS(node t){        memset(in,-1,sizeof(in));        memset(vis,false,sizeof(vis));//false说明可以访问        queue<node> q;        q.push(t);        vis[t.a[0] + (t.a[1]<<7) + (t.a[2]<<14)]=true;        in[t.a[0] + (t.a[1]<<7) + (t.a[2]<<14)]=0;        while(!q.empty()){                node now=q.front();                q.pop();                int nowloc = now.a[0] + (now.a[1]<<7) + (now.a[2]<<14);                //cout << now.a[0] <<' ' << now.a[1] << ' ' << now.a[2] << endl;                for(int i=0;i<3;i++){                        for(int j=0;j<3;j++){                                if(i!=j){                                        node newx;                                        if(can(now,newx,i,j)){                                                //q.push(newx);就算算了之后是可以倒过去的,但是现在仍然不能入队列                                                int newxloc = newx.a[0] + (newx.a[1]<<7) + (newx.a[2]<<14);                                                int res = in[nowloc] + 1;//BFS就是每一步代价都是1的最短路,这里日后还可以改成最短路的                                                if(in[newxloc] == -1 || res < in[newxloc]){                                                        in[newxloc] = res;                                                        if(!vis[newxloc]){                                                                q.push(newx);                                                                vis[newxloc]=true;                                                        }                                                }                                        }                                }                        }                }        }        //分配完成的情况就是最大的两个瓶子里把所有的可乐都平分了        if(c.a[1]>=c.a[2]){                int mid=c.a[0]/2;                if(in[mid + (mid<<7)]==-1) cout << "NO" <<endl;                else cout << in[mid + (mid<<7)] << endl;        }        else{                int mid=c.a[0]/2;                if(in[mid + (mid<<14)]==-1) cout << "NO" <<endl;                else cout << in[mid + (mid<<14)] << endl;        }}int main(){        while(scanf("%d%d%d",&c.a[0],&c.a[1],&c.a[2])==3 && !(c.a[0]==0 && c.a[1]==0 && c.a[2]==0)){                if(c.a[0] % 2 == 1) cout << "NO" << endl;                else{                        node t;                        t.a[0]=c.a[0]; t.a[1]=0; t.a[2]=0;//将饮料的数量初始化                        BFS(t);                }        }        return 0;}
0 0
原创粉丝点击