非常可乐(BFS)

非常可乐

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 17358    Accepted Submission(s): 7042


Problem 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
广度优先搜索,六种情况,判断好往哪里倒,倒的时候又有哪些情况就可以,最后如果两个相等,另一个是零,才是平分,这里判断改了好多次。。。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int n,m,s;//代表了三个瓶子的容量
int vis[120][120][120];//标记三个瓶子的状态是否出现过
struct node{
    int n,m,s;//记录每一次瓶子中可乐的状态
    int step;//记录倒可乐的次数
}rounds,nextrounds;
void Bfs(){//用广度优先搜索
    queue<node>q;
    int i;
    memset(vis,0,sizeof(vis));
    q.push(rounds);
    vis[rounds.s][rounds.n][rounds.m] = 1;
    while(!q.empty()){
        rounds = q.front();
        //printf("s:%d n:%d m:%d\n",rounds.s,rounds.n,rounds.m);
        if((rounds.s==rounds.n&&rounds.m==0)||(rounds.s==rounds.m&&rounds.n==0)||(rounds.m==rounds.n&&rounds.s==0)){//平分,输出次数,返回,注意相等时不能为0,否则一开始0,0程序就结束了
            printf("%d\n",rounds.step);//上面的判断改了很多次,一是一开始两个杯子都是0,是相等的但是不行,所以相等但不为零,二是及时相等相加必须为总数,否则样例1就不会输出no了
            return;
        }
        q.pop();
        for(i = 0; i < 6; i++){//6中情况s->n,n->s,s->m,m->s,n->m,m->n;重点
            if(i==0){//s->n
                if((n-rounds.n)>=rounds.s){//如果n中所剩空间大于等于s中的可乐,就可以把s中的可乐全部倒在n中
                    nextrounds.n = rounds.n + rounds.s;//原有的加上s中的
                    nextrounds.s = 0;//全部倒在n中了
                    nextrounds.m = rounds.m;//另一个瓶子不变
                    nextrounds.step = rounds.step + 1;//次数加一
                }
                else{//如果n所剩空间小于s中的可乐量,则把n倒满,s还剩下一部分
                    nextrounds.n = n;//n倒满了
                    nextrounds.s = rounds.s-(n-rounds.n);//s中剩下部分为原来s减去n中没装满的那一部分
                    nextrounds.m = rounds.m;//不变
                    nextrounds.step = rounds.step + 1;//次数加1
                }
                if(vis[nextrounds.s][nextrounds.n][nextrounds.m]==0){
                    q.push(nextrounds);
                    vis[nextrounds.s][nextrounds.n][nextrounds.m] = 1;
                }
            }
            else if(i==1){//n->s
                if((s-rounds.s)>=rounds.n){//s中所剩空间大于n的可乐量,可以把n中倒完
                    nextrounds.s = rounds.s + rounds.n;
                    nextrounds.n = 0;
                    nextrounds.m = rounds.m;
                    nextrounds.step = rounds.step + 1;
                }
                else{//s中所剩空间小于n中可乐量,可以把s倒满,n中剩余
                    nextrounds.s = s;
                    nextrounds.n = rounds.n - (s-rounds.s);
                    nextrounds.m = rounds.m;
                    nextrounds.step = rounds.step + 1;
                }
                if(vis[nextrounds.s][nextrounds.n][nextrounds.m]==0){
                    q.push(nextrounds);
                    vis[nextrounds.s][nextrounds.n][nextrounds.m] = 1;
                }
            }
            else if(i==2){//s->m
                if((m-rounds.m)>=rounds.s){//m中剩余空间大于s可乐量,s倒完
                    nextrounds.m = rounds.m + rounds.s;
                    nextrounds.s = 0;
                    nextrounds.n = rounds.n;
                    nextrounds.step = rounds.step + 1;
                }
                else{//m剩余空间小于s可乐量,m倒满,s剩余
                    nextrounds.m = m;
                    nextrounds.s = rounds.s - (m-rounds.m);
                    nextrounds.n = rounds.n;
                    nextrounds.step = rounds.step + 1;
                }
                if(vis[nextrounds.s][nextrounds.n][nextrounds.m]==0){
                    q.push(nextrounds);
                    vis[nextrounds.s][nextrounds.n][nextrounds.m] = 1;
                }
            }
            else if(i==3){//m->s
                    if((s-rounds.s)>=rounds.m){//s中剩余空间大于m可乐量,m倒完
                    nextrounds.s = rounds.m + rounds.s;
                    nextrounds.m = 0;
                    nextrounds.n = rounds.n;
                    nextrounds.step = rounds.step + 1;
                }
                else{//s剩余空间小于m可乐量,s倒满,m剩余
                    nextrounds.s = s;
                    nextrounds.m = rounds.m - (s-rounds.s);
                    nextrounds.n = rounds.n;
                    nextrounds.step = rounds.step + 1;
                }
                if(vis[nextrounds.s][nextrounds.n][nextrounds.m]==0){
                    q.push(nextrounds);
                    vis[nextrounds.s][nextrounds.n][nextrounds.m] = 1;
                }
            }
            else if(i==4){//n->m;
                    if((m-rounds.m)>=rounds.n){//m中剩余空间大于n可乐量,n倒完
                    nextrounds.m = rounds.m + rounds.n;
                    nextrounds.n = 0;
                    nextrounds.s = rounds.s;
                    nextrounds.step = rounds.step + 1;
                }
                else{//m剩余空间小于n可乐量,m倒满,n剩余
                    nextrounds.m = m;
                    nextrounds.n = rounds.n - (m-rounds.m);
                    nextrounds.s = rounds.s;
                    nextrounds.step = rounds.step + 1;
                }
                if(vis[nextrounds.s][nextrounds.n][nextrounds.m]==0){
                    q.push(nextrounds);
                    vis[nextrounds.s][nextrounds.n][nextrounds.m] = 1;
                }
            }
            else{//m->n
                    if((n-rounds.n)>=rounds.m){//n中剩余空间大于m可乐量,m倒完
                    nextrounds.n = rounds.n + rounds.m;
                    nextrounds.m = 0;
                    nextrounds.s = rounds.s;
                    nextrounds.step = rounds.step + 1;
                }
                else{//n剩余空间小于m可乐量,n倒满,m剩余
                    nextrounds.n = n;
                    nextrounds.m = rounds.m - (n-rounds.n);
                    nextrounds.s = rounds.s;
                    nextrounds.step = rounds.step + 1;
                }
                if(vis[nextrounds.s][nextrounds.n][nextrounds.m]==0){
                    q.push(nextrounds);
                    vis[nextrounds.s][nextrounds.n][nextrounds.m] = 1;
                }
            }
        }
    }
    printf("NO\n");//如果队列为空了说明方法都用完了,不可能平分
    return;
}
int main(){
    int i;
    while(~scanf("%d%d%d",&s,&n,&m)){
        if(s==0&&n==0&&m==0)break;
        rounds.s = s;
        rounds.n = 0;
        rounds.m = 0;
        rounds.step = 0;
        if(s%2){//奇数肯定不能平分
            printf("NO\n");
        }
        else{
            Bfs();
        }
    }
    return 0;
}


非常抱歉!你说得对 —— 上面的解法虽然逻辑清晰,但**在某些情况下并正确**,因为它**没有正确模拟兑换和可乐的顺序**,也没有处理兑换之间的**最优路径问题**。 我们来**重新分析这个问题**,并给出一个**真正正确的解法**。 --- ## 🧠 正确解法思路:BFS + 状态压缩 + 剪枝 ### 问题重述: - 初始有 `N` 瓶可乐,0 个空瓶,0 个贴纸。 - 每次操作可以是: - 一瓶可乐 → 空瓶数 +1 - 用 `A_i` 个空瓶换 `B_i` 瓶可乐 + 1 贴纸 - 目标是最大化贴纸数。 --- ## ✅ 正确建模方式:BFS / Dijkstra(状态搜索) 我们可以将这个问题建模为一个**状态空间搜索问题**: ### 状态定义: - `state = (empty, cola)`:当前有多少空瓶、有多少可乐 ### 操作定义: - **可乐**:`cola > 0` → `empty += 1`, `cola -= 1` - **兑换**:`empty >= A_i` → `empty -= A_i`, `cola += B_i`, `sticker += 1` ### 状态转移: - 每个状态可以转移到之后的状态或兑换之后的状态 --- ## 🚀 优化策略 - 使用 **Dijkstra 算法** 或 **优先队列 BFS**,以贴纸数最大化为目标 - 每个状态记录最大贴纸数 - 使用哈希表记录已访问状态的最大贴纸数,剪枝无效状态 --- ## ✅ 正确 C++ 实现(BFS + Dijkstra) ```cpp #include <bits/stdc++.h> using namespace std; using ll = long long; using P = pair<ll, ll>; const ll MAX = 1e6; // 用于剪枝的哈希表大小 int main() { ios::sync_with_stdio(false); cin.tie(0); ll N; int M; cin >> N >> M; vector<pair<ll, ll>> exchanges(M); for (int i = 0; i < M; ++i) { ll A, B; cin >> A >> B; exchanges[i] = {A, B}; } // 状态:(empty, cola) → 最大贴纸数 map<pair<ll, ll>, ll> dp; priority_queue<tuple<ll, ll, ll>> pq; // (stickers, empty, cola) // 初始状态:0 empty, N cola, 0 sticker dp[{0, N}] = 0; pq.emplace(0, 0, N); ll ans = 0; while (!pq.empty()) { auto [sticker, empty, cola] = pq.top(); pq.pop(); // 如果当前状态已经是最优的,跳过 if (dp.find({empty, cola}) == dp.end() || dp[{empty, cola}] > sticker) { continue; } ans = max(ans, sticker); // 操作1:一瓶可乐 if (cola > 0) { ll new_empty = empty + 1; ll new_cola = cola - 1; ll new_sticker = sticker; if (dp.find({new_empty, new_cola}) == dp.end() || dp[{new_empty, new_cola}] < new_sticker) { dp[{new_empty, new_cola}] = new_sticker; pq.emplace(new_sticker, new_empty, new_cola); } } // 操作2:尝试每种兑换 for (auto [A, B] : exchanges) { if (empty >= A) { ll new_empty = empty - A; ll new_cola = cola + B; ll new_sticker = sticker + 1; if (dp.find({new_empty, new_cola}) == dp.end() || dp[{new_empty, new_cola}] < new_sticker) { dp[{new_empty, new_cola}] = new_sticker; pq.emplace(new_sticker, new_empty, new_cola); } } } } cout << ans << '\n'; return 0; } ``` --- ## 📌 代码解释 - 使用 `priority_queue` 模拟 Dijkstra 算法,优先扩展贴纸多的状态 - 每次尝试可乐或兑换 - 使用 `map` 记录 `(empty, cola)` → `max sticker`,避免重复搜索 - 最终输出最大贴纸数 --- ## 🧪 示例分析 ### Sample Input 1: ``` 5 3 5 1 4 3 3 1 ``` - 初始状态:(0,5,0) - 最终状态可达:(1,0,3),兑换3次,获得3贴纸 - 输出:`3` --- ## ⚠️ 为什么贪心错误? 贪心策略的问题在于: - 它**能处理兑换顺序同带来的状态差异** - 某些兑换方式可能在某个状态下更好,但贪心无法回溯 - 举例:兑换 A 后可乐得到的空瓶可以兑换 B,但贪心可能直接选择兑换 B 而错过最优路径 --- ## ✅ 时间复杂度分析 - 每个状态 `(empty, cola)` 最多出现一次 - 每个状态最多有两个转移(/兑换) - 最坏情况下状态数约为 `O(N * M)`,但由于剪枝,实际运行很快 --- ## 📚 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值