上古网络流题
为了便于考虑每个决策的贡献,把每个工人的决策拆成N个彼此独立的决策:即其修的倒数第i个车是什么。因为此车后(含此车)有i人要多等T,故贡献为T*n
以此拆点建图,以贡献为费用跑(二分图)费用流即可,由费用流性质可知每人修车顺序必合法
注意本题N,M顺序有点反常
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int maxn=400005; const int inf = 0x3f3f3f3f; int n, m, maxflow, mincost; struct gra { int tm, st[maxn], to[maxn], nx[maxn], c[maxn], d[maxn]; int dis[maxn], inq[maxn], flow[maxn]; int pre[maxn], last[maxn]; queue <int> q; void cle() { tm=-1; memset(st, -1, sizeof(st)); } void adde(int a, int b, int cc, int dd) { //cout<<a<<' '<<' '<<b<<' '<<cc<<' '<<dd<<endl;; tm++; nx[tm]=st[a]; st[a]=tm; to[tm]=b; c[tm]=cc; d[tm]=dd; tm++; nx[tm]=st[b]; st[b]=tm; to[tm]=a; c[tm]=0; d[tm]=-dd; } bool spfa(int s, int t) { memset(dis, 0x3f, sizeof(dis)); memset(inq, 0, sizeof(inq)); memset(flow, 0, sizeof(flow)); memset(pre, 0, sizeof(pre)); memset(last, 0, sizeof(last)); flow[s]=inf; int x, y, i; q.push(s); inq[s]=1; dis[s]=0; pre[t]=-1; while(!q.empty()) { x=q.front(); q.pop(); inq[x]=0; for(i=st[x]; i != -1; i=nx[i]) { y=to[i]; if(c[i] > 0 && dis[y] > dis[x]+d[i]) { dis[y] = dis[x]+d[i]; pre[y]=x; last[y]=i; flow[y]=min(flow[x], c[i]); if(!inq[y]) { inq[y]=1; q.push(y); } } } } return (pre[t] != -1); } void count(int s, int t) { int x; while(spfa(s, t)) { int x=t; maxflow+=flow[t]; mincost+=flow[t]*dis[t]; while(x != s) { c[last[x]]-=flow[t]; c[(last[x]^1)]+=flow[t]; x=pre[x]; } } return; } } G; int main() { ios::sync_with_stdio(false); G.cle(); double ans; int i, j, ta, tb, tc, td, S, T, k; cin>>n>>m; S=n*m+m+1; T=n*m+m+2; for(i=1; i <= n*m; i++) G.adde(S, i, 1, 0); for(i=1; i <= m; i++) G.adde(i+n*m, T, 1, 0); for(i=1; i <= m; i++) { for(j=1; j <= n; j++) { cin>>ta; for(k=1; k <= m; k++) G.adde(m*(j-1)+k, i+n*m, 1, ta*k); } } G.count(S, T); ans=(mincost*1.0)/m; printf("%.2lf ", ans); return 0; }