zoukankan      html  css  js  c++  java
  • #1495:非常可乐(BFS+数论)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1495

    BFS解法

    题目 给三个数字 s n m s=n+m s在1到100之间

    就是个倒水问题可以从第一个倒向第二个 类似的一共可以有六中到发

    现在要求最少经过多少步就能平分那么多水

    首先剪枝是 如果s是奇数必然不行。

    一看到要求最少的步数就知道用bfs了

    我们用vis标记状态

    每个状态有三个整数组成 表示这三个杯子里的可乐数量

    然后对每个状态的递推是 6种 也就是3!种。从一个到到另一个 再标记 入队

    #include<iostream>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int v[5];
    bool sign[110][110][110];
    /*
    我们用sign标记状态
    每个状态有三个整数组成 表示这三个杯子里的可乐数量
    */
    struct cup {
        int v[5];
        int step;
        cup() { v[0] = v[1] = v[2] = v[3] = step = 0; }
    }temp;
    
    void pour(int a, int b)//倒水函数,把a杯子中的可乐倒到b杯子中
    {
        int sum = temp.v[a] + temp.v[b];
        if (sum >= v[b])
            temp.v[b] = v[b];
        else
            temp.v[b] = sum;
        temp.v[a] = sum - temp.v[b];
    }
    
    void bfs()
    {
        int i, j;
        queue<cup>q;
        cup cnt;
        cnt.v[1] = v[1];
        cnt.v[2] = 0;
        cnt.v[3] = 0;
        cnt.step = 0;
        q.push(cnt);
        memset(sign, 0, sizeof(sign));
        sign[v[1]][0][0] = 1;
        while (!q.empty())
        {
            cnt = q.front();
            q.pop();
            if (cnt.v[1] == cnt.v[3] && cnt.v[2] == 0)
            {
                printf("%d
    ", cnt.step);
                return;
            }
            for (i = 1; i < 4; ++i)
            {
                for (j = 1; j < 4; ++j)
                {
                    if (i != j)//自己不倒水给自己  
                    {
                        temp = cnt;//每个水位情况都要把所有操作枚举一遍,所以都要赋值为原始水位情况  
                        pour(i, j);
                        if (!sign[temp.v[1]][temp.v[2]][temp.v[3]])
                        {
                            temp.step++;
                            q.push(temp);
                            sign[temp.v[1]][temp.v[2]][temp.v[3]] = 1;
                        }
                    }
                }
            }
        }
        printf("NO
    ");
    }
    
    int main()
    {
        while (scanf("%d%d%d", &v[1], &v[2], &v[3]) && v[1] || v[2] || v[3])
        {
            if (v[2] > v[3])
                swap(v[2], v[3]);
            bfs();
        }
        return 0;
    }
    

    利用数论的神仙题解

    分析:设两个小瓶子容积分别为a,b,问题转化成通过两个小瓶子的若干次倒进或倒出操作得到(a+b)/2体积的可乐,设两个小瓶子被倒进或倒出x次和y次(这里的x和y是累加后的操作,即x=第一个瓶子倒出的次数-倒进的次数,y=第二个瓶子倒出的次数-倒进的次数),那么问题转化成:

    所以|x+|y|的最小值为(c+d)/2,通过x和y的通解形式显然可以看出x和y一正一负,不妨设x<0,那么就是往第一个小瓶子倒进x次,第二个小瓶子倒出y次,但是由于瓶子容积有限,所以倒进倒出操作都是通过大瓶子来解决的,一次倒进操作后为了继续使用小瓶子还要将小瓶子中可乐倒回大瓶子中,倒出操作同理,所以总操作次数是(c+d)/2*2=c+d,但是注意最后剩下的(a+b)/2体积的可乐一定是放在两个小瓶子中较大的那个中,而不是再倒回到大瓶子中,所以操作数要减一,答案就是c+d-1
    下面给出AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    int gcd(int a,int b)
    {
        return b==0?a:gcd(b,a%b);
    }
    int main()
    {
        int a,b,c;
        while(cin>>a>>b>>c&&(a&&b&&c))
        {
            a/=gcd(b,c);
            if(a&1)
                cout<<"NO"<<endl;
            else
                cout<<a-1<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    [SDOI2006] 保安站岗
    [NOIP2003] 传染病控制
    [USACO13OPEN] 照片Photo
    [HNOI/AHOI2018] 道路
    [TJOI2007] 线段
    [HAOI2009] 逆序对数列
    codeforces|CF1054D Changing Array
    hihoCoder 1785
    luogu 1712
    luogu 3248
  • 原文地址:https://www.cnblogs.com/RioTian/p/12855376.html
Copyright © 2011-2022 走看看