1. 程式人生 > >bzoj3996 [TJOI2015]線性代數 最小割

bzoj3996 [TJOI2015]線性代數 最小割

Description

給出一個N* N的矩陣B和一個1* N的矩陣C。求出一個1* N的01矩陣A使得D=(A*B-C)*AT 最大。其中 AT 為A的轉置。輸出D

1<=N<=500

Solution

我們令E=A*B-C,考慮E中每一位都是什麼,顯然有Ei=Ai[(j=1nAjBi,j)Ci]E_i=A_i\cdot \left[\left(\sum_{j=1}^nA_j\cdot B_{i,j}\right)-C_i\right],我們要求的D就是對E求和,於是有D=i=1nEi=i=1nj=1nA

iAjBi,ji=1nCiAiD=\sum_{i=1}^nE_i=\sum_{i=1}^n\sum_{j=1}^nA_i\cdot A_j\cdot B_{i,j}-\sum_{i=1}^nC_i\cdot A_i 然後我就不會做了

原題等價於從n個物品中選出一些物品,當i、j同時被選則產生B[i,j]的價值,選i則有C[i]的花費,求最大價值。於是這就是一個最小割的模型了

Code

#include <stdio.h>
#include <string.h>
#include <queue> #include <algorithm> #define rep(i,st,ed) for (int i=st;i<=ed;++i) #define fill(x,t) memset(x,t,sizeof(x)) const int INF=0x3f3f3f3f; const int N=250505; const int E=5000005; struct edge {int x,y,w,next;} e[E]; int dis[N],cur[N],b[505][505],c[N]; int ls[N],edCnt=1; int read
() { int x=0,v=1; char ch=getchar(); for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar()); for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar()); return x*v; } void add_edge(int x,int y,int w) { e[++edCnt]=(edge) {x,y,w,ls[x]}; ls[x]=edCnt; e[++edCnt]=(edge) {y,x,0,ls[y]}; ls[y]=edCnt; } bool bfs(int st,int ed) { std:: queue <int> que; que.push(st); rep(i,st,ed) dis[i]=-1; dis[st]=1; for (;!que.empty();) { int now=que.front(); que.pop(); for (int i=ls[now];i;i=e[i].next) { if (e[i].w>0&&dis[e[i].y]==-1) { dis[e[i].y]=dis[now]+1; if (e[i].y==ed) return true; que.push(e[i].y); } } } return false; } int find(int now,int ed,int mn) { if (now==ed||!mn) return mn; int ret=0; for (int &i=cur[now];i;i=e[i].next) { if (e[i].w>0&&dis[now]+1==dis[e[i].y]) { int d=find(e[i].y,ed,std:: min(mn-ret,e[i].w)); e[i].w-=d; e[i^1].w+=d; ret+=d; if (ret==mn) break; } } return ret; } int dinic(int st,int ed) { int ret=0; for (;bfs(st,ed);) { rep(i,st,ed) cur[i]=ls[i]; ret+=find(st,ed,INF); } return ret; } int main(void) { int n=read(),sum=0; rep(i,1,n) rep(j,1,n) b[i][j]=read(),sum+=b[i][j]; rep(i,1,n) c[i]=read(); rep(i,1,n) rep(j,1,n) { add_edge(0,(i-1)*n+j,b[i][j]); add_edge((i-1)*n+j,n*n+i,INF); add_edge((i-1)*n+j,n*n+j,INF); } rep(i,1,n) add_edge(n*n+i,n*n+n+1,c[i]); printf("%d\n", sum-dinic(0,n*n+n+1)); return 0; }