bzoj 3996 [TJOI2015]线性代数——最小割
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3996
b[ i ][ j ] 要计入贡献,当且仅当 a[ i ] = 1 , a[ j ] = 1 ;-c[ i ] 要计入贡献,当且仅当 a[ i ] = 1;所以建一排 b 的点,建一排 a 的点,源点向 b 的点连它们价值容量的边,b 向它对应的两个 a 连 INF ; a 向汇点连它对应的 c 容量的边;割源点到 b 的边表示不选该 b ,割 a 到汇点的边表示选该 a 。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int M=505,N=500*501+5,INF=N*1000; int n,cnt,hd[N],xnt=1,cur[N],ans; int dfn[N],q[N],he,tl;bool vis[N]; struct Ed{ int to,nxt,cap; Ed(int a=0,int b=0,int c=0):to(a),nxt(b),cap(c) {} }ed[N*6]; void add(int x,int y,int z) { ed[++xnt]=Ed(y,hd[x],z);hd[x]=xnt; ed[++xnt]=Ed(x,hd[y],0);hd[y]=xnt; } bool bfs() { memset(dfn,0,sizeof dfn); q[he=tl=1]=0;vis[0]=1;dfn[0]=1; while(he<=tl) { int k=q[he++];vis[k]=0;//he++ for(int i=hd[k],v;i;i=ed[i].nxt) if(!dfn[v=ed[i].to]&&ed[i].cap) dfn[v]=dfn[k]+1,q[++tl]=v; } return dfn[cnt]; } int dinic(int cr,int flow) { if(cr==cnt)return flow; int use=0; for(int& i=cur[cr],v;i;i=ed[i].nxt) if(dfn[v=ed[i].to]==dfn[cr]+1&&ed[i].cap) { int tmp=dinic(v,min(flow-use,ed[i].cap)); if(!tmp)dfn[v]=0; use+=tmp;ed[i].cap-=tmp;ed[i^1].cap+=tmp; if(use==flow)return use; } return use; } int main() { scanf("%d",&n);cnt=n; for(int i=1;i<=n;i++) for(int j=1,d;j<=n;j++) { scanf("%d",&d);cnt++;ans+=d; add(0,cnt,d);add(cnt,i,INF); if(i!=j)add(cnt,j,INF); } cnt++; for(int i=1,d;i<=n;i++) scanf("%d",&d),add(i,cnt,d); while(bfs())memcpy(cur,hd,sizeof hd),ans-=dinic(0,INF); printf("%d\n",ans); return 0; }