Solution:
抽象一下就是:同时选和可以获得的价值,而选了就要失去的价值,求最大获益。
考虑网络流,源点向每一个连一条流量为的边,接着向每一个和分别连一条流量为无限的边。最后每一个再向汇点连一条流量为的边。
我们考虑这样一张流量图的割意味着什么,对于每一个i,如果你想要断去经过它到汇点的路径,要么割掉左边所有相连的,要么割掉右边流量为的边.
记,则F-割的大小就是一组可行的解。所以F-最小割就是最大获益,即求F-最大流。
Code:
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
template<typename T> void read(T &num){
char c=getchar();T f=1;num=0;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){num=(num<<3)+(num<<1)+(c^48);c=getchar();}
num*=f;
}
template<typename T> void qwq(T x){
if(x>9)qwq(x/10);
putchar(x%10+'0');
}
template<typename T> void write(T x){
if(x<0){x=-x;putchar('-');}
qwq(x);putchar('\n');
}
int B[510][510];int C[510];
struct wzy{
int nxt,vertice,w;
}edge[1600010];
int head[260010];int len=1;
inline void add_edge(int x,int y,int z){
edge[++len].nxt=head[x];edge[len].vertice=y;edge[len].w=z;head[x]=len;
edge[++len].nxt=head[y];edge[len].vertice=x;edge[len].w=0;head[y]=len;return;
}
int s=0;int t=0;
queue<int>v;int d[260010];
inline bool bfs(){
memset(d,0,sizeof(d));
while(!v.empty())v.pop();
d[s]=1;v.push(s);
while(!v.empty()){
int x=v.front();v.pop();
for(int i=head[x];i;i=edge[i].nxt){
int nop=edge[i].vertice;
if(d[nop]||!edge[i].w)continue;
d[nop]=d[x]+1;v.push(nop);
if(nop==t)return true;
}
}
return false;
}
inline int dinic(int x,int val){
if(x==t)return val;
int k=0;
for(int i=head[x];i&&k!=val;i=edge[i].nxt){
int nop=edge[i].vertice;
if(!edge[i].w||d[nop]!=d[x]+1)continue;
int temp=dinic(nop,min(val-k,edge[i].w));
if(!temp)d[nop]=0;
edge[i].w-=temp;edge[(i^1)].w+=temp;k+=temp;
}
return k;
}
int main(){
int n;read(n);int ans=0;
rep(i,1,n){
rep(j,1,n)read(B[i][j]);
}
rep(i,1,n)read(C[i]);
s=0;t=n*n+n+1;
rep(i,1,n){
rep(j,1,n){
add_edge(s,(i-1)*n+j,B[i][j]);ans+=B[i][j];
add_edge((i-1)*n+j,n*n+i,INT_MAX);
add_edge((i-1)*n+j,n*n+j,INT_MAX);
}
}
rep(i,1,n)add_edge(n*n+i,t,C[i]);
int max_flow=0;int flow=0;
while(bfs()){
do{
flow=dinic(s,INT_MAX);max_flow+=flow;
}while(flow);
}
write(ans-max_flow);
return 0;
}