【BZOJ2127】happiness(网络流dinic)

题目描述

传送门

题解

最小割,割掉的是舍弃的喜悦值。
从s向每一个人连边,容量为这个人学文的喜悦值;从每个人向t连边,容量为这个人学理的喜悦值。
处理两个人共同学文或学理的喜悦值的话,可以每两个人增加一个结点,然后将两个人都向这个点连边,容量为学文或学理的喜悦值,注意这个点还要向s或t连边,容量也为学文或学理的喜悦值。
即对于每两个有关系的人增加两个结点,分别处理共同学文或者共同学理的情况,限制为各自的喜悦值。注意s方向向人得方向连的边意义都为学文,而从人的方向向t方向连的边意义都为学理。
最终用总的喜悦值减去maxflow即为答案。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;

const int max_n=105;
const int max_m=105;
const int max_N=max_n*max_m+2*((max_n-1)*max_m+max_n*(max_m-1))+2+5;
const int max_e=2*max_n*max_m+3*2*((max_n-1)*max_m+max_n*(max_m-1))+5;
const int inf=1e9;

int n,m,maxflow,N,sum;
int wen[max_n][max_m],li[max_n][max_m],weni[max_n][max_m],lii[max_n][max_m],wenj[max_n][max_m],lij[max_n][max_m];
int cnt,num[max_n][max_m];
int tot,point[max_N],next[max_e*2],v[max_e*2],remain[max_e*2];
int deep[max_N],cur[max_N];
queue <int> q;

inline void add(int x,int y,int cap){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=cap;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0;
}

inline bool bfs(int s,int t){
    memset(deep,0x7f,sizeof(deep));
    deep[s]=0;
    for (int i=1;i<=N;++i)
      cur[i]=point[i];
    while (!q.empty()) q.pop();
    q.push(s);

    while (!q.empty()){
        int now=q.front(); q.pop();
        for (int i=point[now];i!=-1;i=next[i])
            if (deep[v[i]]>inf&&remain[i]){
                deep[v[i]]=deep[now]+1;
                q.push(v[i]);
            }
    }

    return deep[t]<inf;
}

inline int dfs(int now,int t,int limit){
    if (now==t||!limit) return limit;
    int flow=0,f;
    for (int i=cur[now];i!=-1;i=next[i]){
        cur[now]=i;
        if (deep[v[i]]==deep[now]+1&&(f=dfs(v[i],t,min(remain[i],limit)))){
            flow+=f;
            limit-=f;
            remain[i]-=f;
            remain[i^1]+=f;
            if (!limit) break;//
        }
    }
    return flow;
}

inline void dinic(int s,int t){
    while (bfs(s,t))
      maxflow+=dfs(s,t,inf);
}

int main(){
    //read
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
      for (int j=1;j<=m;++j)
        scanf("%d",&wen[i][j]),sum+=wen[i][j];
    for (int i=1;i<=n;++i)
      for (int j=1;j<=m;++j)
        scanf("%d",&li[i][j]),sum+=li[i][j];
    for (int i=1;i<n;++i)
      for (int j=1;j<=m;++j)
        scanf("%d",&weni[i][j]),sum+=weni[i][j];
    for (int i=1;i<n;++i)
      for (int j=1;j<=m;++j)
        scanf("%d",&lii[i][j]),sum+=lii[i][j];
    for (int i=1;i<=n;++i)
      for (int j=1;j<m;++j)
        scanf("%d",&wenj[i][j]),sum+=wenj[i][j];
    for (int i=1;i<=n;++i)
      for (int j=1;j<m;++j)
        scanf("%d",&lij[i][j]),sum+=lij[i][j];

    //build up
    tot=-1;
    memset(point,-1,sizeof(point));
    memset(next,-1,sizeof(next));

    N=n*m+2*((n-1)*m+n*(m-1))+2;
    cnt=1;
    for (int i=1;i<=n;++i)
      for (int j=1;j<=m;++j){
        cnt++;
        num[i][j]=cnt;
        add(1,cnt,wen[i][j]);
        add(cnt,N,li[i][j]);
      }
    for (int i=1;i<n;++i)
      for (int j=1;j<=m;++j){
        cnt++;
        add(1,cnt,weni[i][j]);
        add(cnt,num[i][j],weni[i][j]);
        add(cnt,num[i+1][j],weni[i][j]);
      }
    for (int i=1;i<n;++i)
      for (int j=1;j<=m;++j){
        cnt++;
        add(cnt,N,lii[i][j]);
        add(num[i][j],cnt,lii[i][j]);
        add(num[i+1][j],cnt,lii[i][j]);
      }
    for (int i=1;i<=n;++i)
      for (int j=1;j<m;++j){
        cnt++;
        add(1,cnt,wenj[i][j]);
        add(cnt,num[i][j],wenj[i][j]);
        add(cnt,num[i][j+1],wenj[i][j]);
      }
    for (int i=1;i<=n;++i)
      for (int j=1;j<m;++j){
        cnt++;
        add(cnt,N,lij[i][j]);
        add(num[i][j],cnt,lij[i][j]);
        add(num[i][j+1],cnt,lij[i][j]);
      }

    dinic(1,N);

    printf("%d\n",sum-maxflow);
}

你可能感兴趣的:(网络流,bzoj)