好题,详细题解在这里http://blog.csdn.net/weiguang_123/article/details/8077385
这里回顾一下:
当i和j都在一个集合里会产生新的收益,是经典题直接建
当i和j不在同一个集合产生新的收益,需要点是二分图XY并把X或Y点集s-t反建
这里是类似的(以前的总结:http://www.cnblogs.com/phile/p/4473226.html)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 const int inf=1e9+7; 6 struct way{int po,flow,next;} e[2400010]; 7 int t,n,p[1010],pre[1010],numh[1010],cur[1010],h[1010],d[1010],m,len; 8 9 void add(int x,int y,int f) 10 { 11 e[++len].po=y; 12 e[len].flow=f; 13 e[len].next=p[x]; 14 p[x]=len; 15 } 16 void build(int x, int y, int f) 17 { 18 add(x,y,f); 19 add(y,x,0); 20 } 21 ll sap() 22 { 23 memset(numh,0,sizeof(numh)); 24 memset(h,0,sizeof(h)); 25 numh[0]=t+1; 26 for (int i=0; i<=t; i++) cur[i]=p[i]; 27 int j,u=0,neck=inf; ll s=0; 28 while (h[0]<t+1) 29 { 30 d[u]=neck; 31 bool ch=1; 32 for (int i=cur[u]; i!=-1; i=e[i].next) 33 { 34 j=e[i].po; 35 if (e[i].flow>0&&h[u]==h[j]+1) 36 { 37 neck=min(neck,e[i].flow); 38 cur[u]=i; 39 pre[j]=u; u=j; 40 if (u==t) 41 { 42 s+=neck; 43 while (u) 44 { 45 u=pre[u]; 46 j=cur[u]; 47 e[j].flow-=neck; 48 e[j^1].flow+=neck; 49 } 50 neck=inf; 51 } 52 ch=0; 53 break; 54 } 55 } 56 if (ch) 57 { 58 if (--numh[h[u]]==0) return s; 59 int q=-1,tmp=t; 60 for (int i=p[u]; i!=-1; i=e[i].next) 61 { 62 j=e[i].po; 63 if (e[i].flow&&h[j]<tmp) 64 { 65 tmp=h[j]; 66 q=i; 67 } 68 } 69 cur[u]=q; h[u]=tmp+1; 70 numh[h[u]]++; 71 if (u) 72 { 73 u=pre[u]; 74 neck=d[u]; 75 } 76 } 77 } 78 return s; 79 } 80 81 int main() 82 { 83 int cas; 84 scanf("%d",&cas); 85 while (cas--) 86 { 87 len=-1; 88 memset(p,255,sizeof(p)); 89 scanf("%d",&n); 90 ll ans=0; 91 for (int i=1; i<=n; i++) 92 { 93 int s=0; 94 for (int j=1; j<=n; j++) 95 { 96 int x; 97 scanf("%d",&x); 98 s+=x; ans+=x; 99 build(i,j,x); 100 } 101 build(0,i,s); 102 } 103 t=n+1; 104 for (int i=1; i<=n; i++) 105 { 106 int x; 107 scanf("%d",&x); 108 build(i,t,x); 109 } 110 printf("%lld\n",ans-sap()); 111 } 112 }