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;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/10118523.html