[BZOJ2127]happiness(网络流)

=== ===

这里放传送门

=== ===

题解

非常经典的最小割建图模型啦!考虑最小的放弃的喜悦值,以S代表学文,T代表学理为例,首先对于单个同学来说,他的学文的喜悦值和学理的喜悦值不能同时存在,于是给每个同学建立一个点,从S连到它,流量为它学文的喜悦值;从它连到T,流量为它学理的喜悦值,那么这两条边必定要割掉一条。对于处理两个同学共同学文或者学理的情况,对每一对相邻的同学新建两个点,从S连到其中一个点,这个点再连到那两个同学,流量为这两个同学共同学文的喜悦值;这两个同学连到另外一个点,从这个点再连到T,流量为这两个同学共同学理的喜悦值。
这里写图片描述

搞出来就大概长这样,妈呀这图画起来真费事= =

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define inc(x)(x=(x%60000)+1)
#define inf 1000000000
using namespace std;
int n,m,p[60010],tot,S,T,N,cnt,cur[60010],gap[60010],d[60010],pre[60010],sum;
struct edge{
    int to,flw,nxt;
}e[3000010];
int cal(int i,int j){
    return (i-1)*m+j;
}
void add(int from,int to,int flow){
    e[tot].to=to;
    e[tot].flw=flow;
    e[tot].nxt=p[from];
    p[from]=tot;
    ++tot;
}
void Bfs(int N){
    int q[60010],head,tail;
    for (int i=0;i<=N;i++)
      d[i]=N+1;
    memset(q,0,sizeof(q));
    head=0;tail=1;
    q[tail]=T;
    d[T]=0;
    while (head!=tail){
        int u;
        inc(head);
        u=q[head];
        for (int i=p[u];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if ((e[i^1].flw>0)&&(d[v]==N+1)){
                d[v]=d[u]+1;
                inc(tail);q[tail]=v;
            }
        }
    }
}
int track(){
    int Min=inf;
    for (int i=T;i!=S;i=e[pre[i]^1].to)
      Min=min(Min,e[pre[i]].flw);
    for (int i=T;i!=S;i=e[pre[i]^1].to){
        e[pre[i]].flw-=Min;
        e[pre[i]^1].flw+=Min;
    }
    return Min;
}
int ISAP(int N){
    int u=S,Flow=0;
    bool flag=false;
    Bfs(N);
    for (int i=0;i<=N;i++)
      cur[i]=p[i];
    memset(gap,0,sizeof(gap));
    memset(pre,0,sizeof(pre));
    for (int i=0;i<=N;i++)
      ++gap[d[i]];
    while (d[S]<N){
        if (u==T){
            Flow+=track();
            u=S;
        }
        flag=false;
        for (int i=cur[u];i!=-1;i=e[i].nxt){
            int v=e[i].to;
            if ((e[i].flw>0)&&(d[v]+1==d[u])){
                flag=true;
                pre[v]=i;
                cur[u]=i;//更新当前弧
                u=v;
                break;
            }
        }
        if (flag==false){
            int v=N-1;
            for (int i=p[u];i!=-1;i=e[i].nxt)
              if (e[i].flw>0)
                v=min(v,d[e[i].to]);
            --gap[d[u]];
            if (gap[d[u]]==0) break;
            d[u]=v+1;
            ++gap[d[u]];
            cur[u]=p[u];//初始化这个点对应的当前弧
            if (u!=S)
              u=e[pre[u]^1].to;
        }
    }
    return Flow;
}
int main()
{
    memset(p,-1,sizeof(p));
    scanf("%d%d",&n,&m);
    N=n*m;tot=cnt=0;
    S=0;T=N*5+1;
    sum=0;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++){
          int x;
          ++cnt;
          scanf("%d",&x);
          sum+=x;//计算总的喜悦值
          add(S,cnt,x);add(cnt,S,0);//建立每个同学对于学文的喜悦值
      }
    cnt=0;
    for (int i=1;i<=n;i++)
      for (int j=1;j<=m;j++){
          int x;
          ++cnt;
          scanf("%d",&x);
          sum+=x;
          add(cnt,T,x);add(T,cnt,0);//建立每个同学对于学理的喜悦值
      }
    for (int i=1;i<n;i++)
      for (int j=1;j<=m;j++){
          int x,pt;
          cnt=cal(i,j); 
          pt=cnt+N;//计算第一组点的编号
          scanf("%d",&x);
          sum+=x;
          add(S,pt,x);add(pt,S,0);
          add(pt,cnt,x);add(cnt,pt,0);
          add(pt,cnt+m,x);add(cnt+m,pt,0);
      }
    for (int i=1;i<n;i++)
      for (int j=1;j<=m;j++){
          int x,pt;
          cnt=cal(i,j);
          pt=cnt+2*N;
          scanf("%d",&x);
          sum+=x;
          add(pt,T,x);add(T,pt,0);
          add(cnt,pt,x);add(pt,cnt,0);
          add(cnt+m,pt,x);add(pt,cnt+m,0);
      }
    for (int i=1;i<=n;i++)
      for (int j=1;j<m;j++){
          int x,pt;
          cnt=cal(i,j);
          pt=cnt+3*N;
          scanf("%d",&x);
          sum+=x;
          add(S,pt,x);add(pt,S,0);
          add(pt,cnt,x);add(cnt,pt,0);
          add(pt,cnt+1,x);add(cnt+1,pt,0);
      }
    for (int i=1;i<=n;i++)
      for (int j=1;j<m;j++){
          int x,pt;
          cnt=cal(i,j);
          pt=cnt+4*N;
          scanf("%d",&x);
          sum+=x;
          add(pt,T,x);add(T,pt,0);
          add(cnt,pt,x);add(pt,cnt,0);
          add(cnt+1,pt,x);add(pt,cnt+1,0);
      }
    printf("%d\n",sum-ISAP(T));
    return 0;
}

偏偏在最后出现的补充说明

做最小割这样的题的时候一定要分清楚建图是关于舍弃的价值还是得到的价值,愚蠢的ATP好几次都把这两个像和稀泥一样混到一起然后图就建成了一坨shi怎么也跑不动= =

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值