题目:洛谷P3973、BZOJ3996。
题目大意:
给你n,参数b[][]和c[](里面的数均>0),要你求一个数组A[](0/1,1表示选择),已知:
1. 若同时选择X和Y,获得B[x][y]收益
2. 若选择了X,需要C[x]的代价
求最大的收益
解题思路:
我们将第一个条件转化为:若不同时选择X和Y,需要B[x][y]的代价。
然后,建最小割模。
将(i,j)看成一个点,从S向其连容量为B[i][j]+B[j][i](若i=j则只连B[i][j])。
将每个i看成一个点,向T连容量为C[i]的边。
然后因为选(i,j)必须要选i和j,所以从(i,j)分别向i和j连容量∞的边(i=j则只连一条)。
求出最小割,然后答案就是sum(B)-最小割。
C++ Code:
#include<bits/stdc++.h> #define N 1005 #define INF 0x3f3f3f3f int dis[N*N+N+5],iter[N*N+N+5],cnt=1,head[N*N+N+5],B[505][505],C[505],n,sm=0,level[N*N+N+5],q[7000005]; bool vis[N*N+N+5]; struct edge{ int from,to,cap,nxt; }e[(N*N+N+5)<<2]; inline int readint(){ int c=getchar(),d=0; for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^'0'); return d; } inline void addedge(const int from,const int to,const int cap){ e[++cnt]=(edge){from,to,cap,head[from]}; head[from]=cnt; e[++cnt]=(edge){to,from,0,head[to]}; head[to]=cnt; } void bfs(int s){ memset(level,-1,sizeof(level)); level[s]=0; int l=0,r=1; q[1]=s; while(l!=r){ int u=q[l=l%7000000+1]; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(level[v]<0&&e[i].cap>0){ level[v]=level[u]+1; q[r=r%7000000+1]=v; } } } } int dfs(int u,int t,int f){ if(u==t)return f; for(int& i=iter[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(e[i].cap>0&&level[v]>level[u]){ int d=dfs(v,t,std::min(f,e[i].cap)); if(d){ e[i].cap-=d; e[i^1].cap+=d; return d; }else level[v]=-1; } } return 0; } int max_flow(int s,int t){ int flow=0; while(1){ bfs(s); if(level[t]<0)return flow; memcpy(iter,head,sizeof(iter)); int f; while(f=dfs(s,t,INF))flow+=f; } } int main(){ n=readint(); memset(head,-1,sizeof head); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)sm+=(B[i][j]=readint()); for(int i=1;i<=n;++i)C[i]=readint(); for(int i=1;i<=n;++i) for(int j=1;j<=n;++j){ int flow=(i==j)?B[i][i]:B[i][j]+B[j][i]; addedge(0,(i-1)*n+j,flow); addedge((i-1)*n+j,n*n+i,INF); if(i!=j)addedge((i-1)*n+j,n*n+j,INF); } for(int i=1;i<=n;++i)addedge(n*n+i,n*n+n+1,C[i]); printf("%d ",sm-max_flow(0,n*n+n+1)); }