bzoj 3996: [TJOI2015]线性代数

Description

给出一个N*N的矩阵B和一个1*N的矩阵C。求出一个1*N的01矩阵A.使得

D=(A*B-C)*A^T最大。其中A^T为A的转置。输出D

Input

第一行输入一个整数N,接下来N行输入B矩阵,第i行第J个数字代表Bij.
接下来一行输入N个整数,代表矩阵C。矩阵B和矩阵C中每个数字都是不超过1000的非负整数。

Output

输出最大的D

Sample Input

3
1 2 1
3 1 0
1 2 3
2 3 7

Sample Output

2

HINT

 1<=N<=500

Source

经过推导得出:


 

是一个最大权闭合子图的模型

选择一个Ai==1,会损失Ci;

对于一个点对(i,j),当Ai和Aj同时==1时,可以获得Bij的收益;

由于收益是同时依赖于两个点的,所以可以对每一个点对新建一个附加点tt,从s向其连Bij的边,然后tt向i,j连Inf;

其余的连边就是最大权闭合子图的套路了

最后正权和-最小割即为答案

(玄学剪枝真有用)

// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define RG register
using namespace std;
typedef long long ll;
const int N=2000000;
const int Inf=19260817;
int gi()
{
  int x=0;
  char ch=getchar();
  while(ch<'0'||ch>'9') ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
int head[N],nxt[N],to[N],s[N],cnt=1,S,T,n,sum,q[N],level[N],vis[N],F,c[N];
int b[600][600],C[1000],tot;
inline void Addedge(int x,int y,int z) {
  to[++cnt]=y,s[cnt]=z,nxt[cnt]=head[x],head[x]=cnt;
}
inline void lnk(int x,int y,int z){
  Addedge(x,y,z);Addedge(y,x,0);
}
inline bool bfs(){
  for(RG int i=S;i<=T;i++) level[i]=0,vis[i]=0;
  int t=0,sum=1;
  q[0]=S,level[S]=1,vis[S]=1;
  while(t<sum){
      int now=q[t++];
      if(now==T) return 1;
      for(RG int i=head[now];i;i=nxt[i]){
      int y=to[i];
      if(level[y]==0&&s[i]){
          level[y]=level[now]+1;
          q[sum++]=y;
        }
    }
    }
  return 0;
}
inline int dfs(int now,int maxf){
  if(now==T) return maxf;
  int ret=0;
  for(RG int i=head[now];i;i=nxt[i]) {
    int y=to[i],f=s[i];
    if(level[y]==level[now]+1&&f) {
      int minn=min(maxf-ret,f);
      f=dfs(y,minn);
      s[i]-=f;
      s[i^1]+=f;ret+=f;
      if(ret==maxf) break;
    }
  }
  if(!ret) level[now]=0;
  return ret;
}
inline void Dinic(){
    while(bfs()) F+=dfs(S,Inf);
}
int main(){
    n=gi();
    for(RG int i=1;i<=n;i++)
    for(RG int j=1;j<=n;j++) b[i][j]=gi();
    for(RG int i=1;i<=n;i++) C[i]=gi(),tot+=C[i];
    S=0,T=n+n*n+1;int ans=0,tt=n;
    for(RG int i=1;i<=n;i++) lnk(i,T,C[i]);
    for(RG int i=1;i<=n;i++){
    for(RG int j=1;j<=n;j++){
        tt++;lnk(S,tt,b[i][j]);ans+=b[i][j];
        lnk(tt,i,Inf);lnk(tt,j,Inf);
    }
    }
    Dinic();printf("%d\n",ans-F);
    return 0;
}

  

posted @ 2017-05-08 13:15  qt666  阅读(143)  评论(0编辑  收藏  举报