非常可乐(BFS和最短路)

原题链接
非常可乐
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
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

门豪杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值