bzoj 1449 [JSOI2009]球队收益(费用拆分,最小费用流)

1449: [JSOI2009]球队收益

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 547  Solved: 302
[Submit][Status][Discuss]

Description

Input

Output

一个整数表示联盟里所有球队收益之和的最小值。

Sample Input

3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1

Sample Output

43

HINT


Source

【思路】

       费用拆分,最小费用最大流。

       由S向每场比赛连边(1,0),由每场比赛向两支队伍连边(1,0)。

       考虑在胜w场败l场的基础上再赢一场,则增加cost = ci*w^2+di*l^2-ci*(w+1)^2-di*(l+1)^2=2*ci*w+ci+di+2*di*l的收益。当w增加时cost是单调递增的,所以可以每次相应连一条(1,cost)的边然后w++,l--再次添边直到比赛全部胜利,初始时假设剩余比赛全输。

【代码】

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<vector>
  5 #define FOR(a,b,c) for(int a=(b);a<(c);a++)
  6 using namespace std;
  7 
  8 typedef long long LL ;
  9 const int maxn = 8000+10;
 10 const int INF = 1e9;
 11 
 12 struct Edge{ int u,v,cap,flow,cost;
 13 };
 14 struct zkw {
 15     int n,m,s,t;
 16     int vis[maxn],d[maxn];
 17     vector<int> G[maxn];
 18     vector<Edge> es;
 19     
 20     void init(int n) {
 21         this->n=n;
 22         es.clear();
 23         for(int i=0;i<n;i++) G[i].clear();
 24     }
 25     void AddEdge(int u,int v,int cap,int cost) {
 26         es.push_back((Edge){u,v,cap,0,cost});
 27         es.push_back((Edge){v,u,0,0,-cost});
 28         m=es.size();
 29         G[u].push_back(m-2);
 30         G[v].push_back(m-1);
 31     }
 32     bool spfa() {
 33         memset(vis,0,sizeof(vis));
 34         for(int i=0;i<n;i++) d[i]=INF;
 35         queue<int> q;
 36         d[t]=0 , vis[t]=1 , q.push(t);
 37         while(!q.empty()) {
 38             int u=q.front(); q.pop() , vis[u]=0;
 39             for(int i=0;i<G[u].size();i++) {
 40                 Edge& e=es[G[u][i]];
 41                 int v=e.v;
 42                 if(es[G[u][i]^1].cap && d[v]>d[u]-e.cost) {
 43                     d[v]=d[u]-e.cost;
 44                     if(!vis[v]) {
 45                         vis[v]=1;
 46                         q.push(v);
 47                     }
 48                 }
 49             }
 50         }
 51         return d[s]!=INF;
 52     }
 53     int dfs(int u,int a,LL& cost) {
 54         vis[u]=1;  if(u==t) return a;
 55         int used=0,w;
 56         for(int i=0;i<G[u].size();i++) {
 57             Edge& e=es[G[u][i]];
 58             int v=e.v;
 59             if(d[u]-e.cost==d[v] && !vis[v] && e.cap) {
 60                 w=dfs(v,min(a-used,e.cap),cost);
 61                 cost+=w*e.cost;
 62                 e.cap-=w , es[G[u][i]^1].cap+=w;
 63                 used+=w; if(used==a) return a;
 64             }
 65         }
 66         return used;
 67     }
 68     int Mincost(int s,int t,LL& cost) {
 69         this->s=s , this->t=t;
 70         int flow=0; cost=0;
 71         while(spfa()) {
 72             vis[t]=1;
 73             while(vis[t]) {
 74                 memset(vis,0,sizeof(vis));
 75                 flow+=dfs(s,INF,cost);
 76             }
 77         }
 78         return flow;
 79     }
 80 } mc;
 81 
 82 
 83 int n,m;
 84 int c[maxn],d[maxn],win[maxn],lose[maxn],in[maxn];
 85 
 86 int main() {
 87     scanf("%d%d",&n,&m);
 88     mc.init(n+m+2);
 89     int s=n+m,t=s+1;
 90     FOR(i,0,n)
 91         scanf("%d%d%d%d",&win[i],&lose[i],&c[i],&d[i]);
 92     int u,v;
 93     FOR(i,0,m) {
 94         scanf("%d%d",&u,&v);
 95         u--,v--; 
 96         in[u]++,in[v]++;
 97         mc.AddEdge(s,i,1,0);
 98         mc.AddEdge(i,m+u,1,0);
 99         mc.AddEdge(i,m+v,1,0);
100     }
101     LL ans=0;
102     FOR(i,0,n) {
103         lose[i]+=in[i];
104         ans+=c[i]*win[i]*win[i]+d[i]*lose[i]*lose[i];
105     }
106     FOR(i,0,n) {
107         FOR(j,0,in[i]) {
108             mc.AddEdge(m+i,t,1,2*c[i]*win[i]+c[i]+d[i]-2*d[i]*lose[i]);
109             lose[i]-- , win[i]++;
110         }
111     }
112     LL cost;
113     mc.Mincost(s,t,cost);
114     printf("%lld",cost+ans);
115     return 0;
116 }
原文地址:https://www.cnblogs.com/lidaxin/p/5094858.html