1. 程式人生 > 實用技巧 >2019-2020 ACM-ICPC Brazil Subregional Programming Contest G. Getting Confidence (最小費用最大流)

2019-2020 ACM-ICPC Brazil Subregional Programming Contest G. Getting Confidence (最小費用最大流)

2019-2020 ACM-ICPC Brazil Subregional Programming Contest G. Getting Confidence


題意

給定一個\(n\times n\)的圖,每個點有一個正整數\(V\)

要求構造出一個排列\(\{P\}\),使得所有的\(V_{P_i,i}\)相乘的結果最大,輸出這個排列

換句話說,也就是選出\(n\)個數字,每行每列只能選一個,且數字相乘結果最大,最後從左到右輸出每個數字的行號


限制

\(1\le n\le 100\)

\(1\le V_{i,j}\le 100\)




思路

根據每行每列只能選一個這個條件,明顯可以將行與列拆點

要求相乘結果最大

,換言之對於所有數取對數後,相加的結果應為最大

於是就可以直接套最小費用最大流跑一遍

源點向所有行點連邊,流量為\(1\)費用為\(0\),表示每個行點只能選擇一次

所有列點向匯點連邊,流量為\(1\)費用為\(0\),表示每個列點只能選擇一次

每個行點\(i\)向每個列點\(j\)連邊,流量為\(1\)費用為\(-log\ V_{i,j}\),表示這種組合產生的花費,取反以達到求最大費用的結果

跑完整張圖後,遍歷與行點相連的殘量網路上的邊,如果某條邊流量為\(0\)即代表被使用過,將其與連向的列點組合,最後輸出答案




程式

(46ms/1000ms)

// StelaYuri
//#include<ext/pb_ds/assoc_container.hpp>
//#include<ext/pb_ds/hash_policy.hpp>
#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define pb push_back
#define eb emplace_back
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const double angcst=PI/180.0;
const ll mod=998244353;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}

const int maxn=233;

struct MCMF {
    struct E {
        int from, to, cap;
        double v;
        E() {}
        E(int f, int t, int cap, double v) : from(f), to(t), cap(cap), v(v) {}
    };
    int n, m, s, t;
    vector<E> edges;
    vector<int> G[maxn];
    bool inq[maxn];
    double dis[maxn];
    int pre[maxn], a[maxn];
    void init(int _n, int _s, int _t) {
        n = _n; s = _s; t = _t;
        for (int i = 0; i <= n; i++)
            G[i].clear();
        edges.clear();
        m = 0;
    }
    void add(int from, int to, int cap, double cost) {
        edges.emplace_back(from, to, cap, cost);
        edges.emplace_back(to, from, 0, -cost);
        G[from].push_back(m++);
        G[to].push_back(m++);
    }
    bool spfa() {
        for (int i = 0; i <= n; i++) {
            dis[i] = 1e9;
            pre[i] = -1;
            inq[i] = false;
        }
        dis[s] = 0, a[s] = 1e9, inq[s] = true;
        queue<int> Q; Q.push(s);
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = false;
            for (int& idx: G[u]) {
                E& e = edges[idx];
                if (e.cap && dis[e.to] > dis[u] + e.v) {
                    dis[e.to] = dis[u] + e.v;
                    pre[e.to] = idx;
                    a[e.to] = min(a[u], e.cap);
                    if (!inq[e.to]) {
                        inq[e.to] = true;
                        Q.push(e.to);
                    }
                }
            }
        }
        return pre[t] != -1;
    }
    double solve() {
        int flow = 0;
        double cost = 0;
        while (spfa()) {
            flow += a[t];
            cost += a[t] * dis[t];
            int u = t;
            while (u != s) {
                edges[pre[u]].cap -= a[t];
                edges[pre[u] ^ 1].cap += a[t];
                u = edges[pre[u]].from;
            }
        }
        return cost;
    }
}f;

int n;
double v[110][110];

void solve()
{
    cin>>n;
    rep(i,1,n)
        rep(j,1,n)
        {
            cin>>v[i][j];
            v[i][j]=log(v[i][j]);
        }
    f.init(202,201,202);
    rep(i,1,n)
    {
        f.add(201,i,1,0);
        f.add(i+100,202,1,0);
    }
    rep(i,1,n)
        rep(j,1,n)
            f.add(i,j+100,1,-v[i][j]);
    f.solve();
    int ans[105];
    rep(i,1,n)
    {
        for(int &id:f.G[i])
        {
            if(f.edges[id].cap==0)
            {
                ans[f.edges[id].to-100]=i;
                break;
            }
        }
    }
    rep(i,1,n)
        cout<<ans[i]<<(i==n?'\n':' ');
}
int main()
{
    closeSync;
    //multiCase
    {
        solve();
    }
    return 0;
}