zoukankan      html  css  js  c++  java
  • [Codeforces Round #674 (Div. 3)] E. Rock, Paper, Scissors (网络流)

    [Codeforces Round #674 (Div. 3)] E. Rock, Paper, Scissors (网络流)

    题面:

    题意:

    给定两个玩家的石头剪刀布的分别卡片个数(每一个人的卡片总和为(mathit n)),

    接下来就进行(mathit n)局游戏,每一局游戏双方(Alice and Bob ) 从自己的卡片中选出一个卡片放在桌子上,并按照常规的剪刀石头布法则觉得胜,负和平局。卡片不可回收。

    现在要计算Alice 最小可以赢多少次,和最多可以赢多少次。

    思路:

    Alice 最多可以赢多少次,这是一个经典的贪心问题,将其卡片尽量按照相克的出即可。

    主要难点在于最小可以赢多少次,有许多暴力枚举所有组合,贪心计算性质等解法,

    这里我讲解一个网络流的解法。

    这样见图:

    源点(mathit S) 对Alice的3种卡片(Rock,Paper,Scissors各自建一个节点)建立容量为卡片个数,成本为0的边。

    Alice的3种卡片节点对Bob的3种卡片节点建立容量为两个卡片个数中较小值,成本为0/1的边。

    具体规则:当Alice的卡片赢取Bob的卡片时,成本为1,否则0。

    Bob的3种卡片节点对汇点(mathit T) 建立 容量为卡片数量,成本为0的边。

    然后求出源点到汇点的最小费用最大流的费用就是Alice最小可以赢的次数。

    思路:

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <bits/stdc++.h>
    #define ALL(x) (x).begin(), (x).end()
    #define sz(a) int(a.size())
    #define rep(i,x,n) for(int i=x;i<n;i++)
    #define repd(i,x,n) for(int i=x;i<=n;i++)
    #define pii pair<int,int>
    #define pll pair<long long ,long long>
    #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    #define MS0(X) memset((X), 0, sizeof((X)))
    #define MSC0(X) memset((X), '', sizeof((X)))
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define eps 1e-6
    #define chu(x)  if(DEBUG_Switch) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
    #define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
    #define du2(a,b) scanf("%d %d",&(a),&(b))
    #define du1(a) scanf("%d",&(a));
    using namespace std;
    typedef long long ll;
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
    ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
    ll poww(ll a, ll b) { if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a ;} a = a * a ; b >>= 1;} return ans;}
    void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("
    ");}}}
    void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("
    ");}}}
    inline long long readll() {long long tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') { fh = -1; } c = getchar();} while (c >= '0' && c <= '9') { tmp = tmp * 10 + c - 48, c = getchar(); } return tmp * fh;}
    inline int readint() {int tmp = 0, fh = 1; char c = getchar(); while (c < '0' || c > '9') {if (c == '-') { fh = -1; } c = getchar();} while (c >= '0' && c <= '9') { tmp = tmp * 10 + c - 48, c = getchar(); } return tmp * fh;}
    void pvarr_int(int *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%d%c", arr[i], i == n ? '
    ' : ' ');}}
    void pvarr_LL(ll *arr, int n, int strat = 1) {if (strat == 0) {n--;} repd(i, strat, n) {printf("%lld%c", arr[i], i == n ? '
    ' : ' ');}}
    const int maxn = 1000010;
    // const int inf = 0x3f3f3f3f;
    /*** TEMPLATE CODE * * STARTS HERE ***/
    #define DEBUG_Switch 0
    const int MAX_N = 200;
    const int MAX_M = 30000;
    const ll inf = 1e18;
    
    struct edge {
        int v; ll c, w; int  next; // v 表示边的另一个顶点,c 表示当前剩余容量,w 表示单位流量费用
    };
    struct MCMF {
        edge e[MAX_M];
        int p[MAX_N], s, t, eid;  // s 表示源点,t 表示汇点,需要在进行 costflow 之前设置完毕
        void init()
        {
            memset(p, -1, sizeof(p));
            eid = 0;
        }
        void insert(int u, int v, ll c, ll w)
        {
            e[eid].v = v;
            e[eid].c = c;
            e[eid].w = w;
            e[eid].next = p[u];
            p[u] = eid++;
        }
        void addedge(int u, int v, ll c, ll w)
        {
            insert(u, v, c, w);
            insert(v, u, 0, -w);
        }
        bool vis[MAX_N];
        ll d[MAX_N];  // 如果到顶点 i 的距离是 inf,则说明不存在源点到 i 的最短路
        // int - memset-> 0x3f  ll - memset-> 0x7f
        int pre[MAX_N];  // 最短路中连向当前顶点的边的编号
        bool dijk()    // 以源点 s 为起点计算单源最短路,如果不存在从 s 到 t 的路径则返回 false,否则返回 true
        {
            memset(d, 0x7f, sizeof(d));
            memset(pre, -1, sizeof(pre));
            d[s] = 0;
            priority_queue<pll, vector<pll>, greater<pll> > q;
            q.push(mp(0, s));
            while (!q.empty()) {
                pll temp = q.top();
                q.pop();
                int u = temp.se;
                if (u == t || d[u] < temp.fi) {
                    continue;
                }
                for (int i = p[u]; i != -1; i = e[i].next) {
                    if (e[i].c) {
                        int v = e[i].v;
                        if (d[u] + e[i].w < d[v]) {
                            d[v] = d[u] + e[i].w;
                            pre[v] = i;
                            q.push(mp(d[v], v));
                        }
                    }
                }
            }
            return pre[t] != -1;
        }
        ll min_cost_flow()    // 计算最小费用最大流
        {
            ll ret = 0;  // 累加和
            while (dijk()) {
                ll flow = inf;
                for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
                    flow = min(e[pre[i]].c, flow);  // 计算当前增广路上的最小流量
                }
                for (int i = t; i != s; i = e[pre[i] ^ 1].v) {
                    e[pre[i]].c -= flow;     //容量是一定要跟着变化的,毕竟要继续循环使用dijk来更新下一条“能走的(看容量)”最短路。
                    e[pre[i] ^ 1].c += flow;
                    ret += e[pre[i]].w * flow;
                }
            }
            return ret;
        }
    } gao;
    int Highestscore(int n, int p1, int q1, int m1, int p2, int q2, int m2)
    {
        // write code here
        int cnt1 = min(p1, q2);
        p1 -= cnt1;
        q2 -= cnt1;
        int cnt2 = min(q1, m2);
        q1 -= cnt2;
        m2 -= cnt2;
        int cnt3 = min(m1, p2);
        m1 -= cnt3;
        p2 -= cnt3;
    
        int cnt = cnt1 + cnt2 + cnt3;
        int score = cnt;
        cnt += min(p1, p2) + min(q1, q2) + min(m1, m2);
        // score -= (n - cnt);
        return score;
    }
    int main()
    {
    #if DEBUG_Switch
        freopen("D:\code\input.txt", "r", stdin);
    #endif
        //freopen("D:\code\output.txt","w",stdout);
        int n, a1, a2, a3, b1, b2, b3;
        cin >> n;
        cin >> a1 >> a2 >> a3;
        cin >> b1 >> b2 >> b3;
        gao.init();
        gao.s = 0;
        gao.t = 7;
        gao.addedge(0, 1, a1, 0);
        gao.addedge(0, 2, a2, 0);
        gao.addedge(0, 3, a3, 0);
        gao.addedge(4, 7, b1, 0);
        gao.addedge(5, 7, b2, 0);
        gao.addedge(6, 7, b3, 0);
        gao.addedge(1, 4, a1, 0);
        gao.addedge(1, 5, a1, 1);
        gao.addedge(1, 6, a1, 0);
        gao.addedge(2, 4, a2, 0);
        gao.addedge(2, 5, a2, 0);
        gao.addedge(2, 6, a2, 1);
        gao.addedge(3, 4, a3, 1);
        gao.addedge(3, 5, a3, 0);
        gao.addedge(3, 6, a3, 0);
    
        cout << gao.min_cost_flow() << " " << Highestscore(n, a1, a2, a3, b1, b2, b3)  << endl;
        return 0;
    }
    
     
    
    本博客为本人原创,如需转载,请必须声明博客的源地址。 本人博客地址为:www.cnblogs.com/qieqiemin/ 希望所写的文章对您有帮助。
  • 相关阅读:
    腾讯云COS对象存储的简单使用
    SpringSecurity的简单使用
    AngularJS入门 & 分页 & CRUD示例
    使用 Java 发送邮件
    Java操作pdf: JarsperReport的简单使用
    获取Spring容器中的Bean协助调试
    JMS--ActiveMQ的简单使用
    Echarts和Quartz简介
    WebService
    Java使用 POI 操作Excel
  • 原文地址:https://www.cnblogs.com/qieqiemin/p/13765487.html
Copyright © 2011-2022 走看看