2019-2020 ACM-ICPC Brazil Subregional Programming Contest G. Getting Confidence (最小費用最大流)
阿新 • • 發佈:2021-01-21
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; }