BZOJ3996 线性代数
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3996
转化题目给的条件
$$D = \sum_{i=1}^n \sum_{j=1}^n{A(i)A(j)B(i,j)} - \sum_{i=1}^n C(i)A(i)$$
网络流可解,如果要得到 $B(i,j)$ 的话必须选$i$物品和$j$物品然后,选择每一个物品都有其代价。
最大权闭合子图。
再流网络中割掉一条边就是舍弃一个$B(i,j)$
所以最小割即可。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define N 1100010 #define INF 0x3f3f3f3f using namespace std; struct edge{ int x,to,cap; }E[N<<2]; int n,B[1010][1010],C[N],totE,g[N],S,T,h[N],tot; queue<int> q; bool v[N]; #define p E[i].x void ade(int x,int y,int cap){ E[++totE]=(edge){y,g[x],cap}; g[x]=totE; E[++totE]=(edge){x,g[y],0}; g[y]=totE; } bool bfs(){ memset(v,0,sizeof(v)); q.push(S); h[S]=1; v[S]=1; while(!q.empty()){ int x=q.front(); q.pop(); for(int i=g[x];i;i=E[i].to) if(E[i].cap&&!v[p]){ h[p]=h[x]+1; q.push(p); v[p]=1; } } return v[T]; } int dinic(int x,int flow){ if(x==T||!flow) return flow; int f=0; for(int i=g[x];i&&flow;i=E[i].to) if(h[p]==h[x]+1&&E[i].cap){ int tmp=dinic(p,min(flow,E[i].cap)); E[i].cap-=tmp; E[i^1].cap+=tmp; f+=tmp; flow-=tmp; } if(!f) h[x]=-1; return f; } int main(){ scanf("%d",&n); for(int i=1,j;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&B[i][j]); for(int i=1;i<=n;i++) scanf("%d",&C[i]); S=n*n+n+1; T=S+1; for(int i=1;i<=n;i++) ade(i,T,C[i]); int ans=0; tot=n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ ans+=B[i][j]; ade(S,++tot,B[i][j]); ade(tot,j,INF); ade(tot,i,INF); } while(bfs()) ans-=dinic(S,INF); printf("%d\n",ans); return 0; }
看来真的需要补线性代数呀。