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;
}