题目:洛谷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\n",sm-max_flow(0,n*n+n+1));
}