BZOJ 2127 : happiness 疯了的最小割

BZOJ 2127 : happiness 疯了的最小割

题目传送门
PS:幸好,先做了 阿狸和桃子的游戏 那一题,不然,想的脑壳teng。

【问题描述】

  一群人选文理科,每人选文选里都有收益,相邻两个人同时选文和选理也有收益。
  最大化收益。

【解题思路】

  选文选理,二选一。
  把所有的收益加起来,减去尽可能少的损失。
  看起来像是网络流-最小割(其实就是)
  参考bzoj2563阿狸和桃子的游戏
  把边的权值,试图把边的权值分离在点上
  就成了这样。。
  两者不选文,则把文的都割掉。
  两者不选理,则把理的都割掉。
这里写图片描述

  那一边割理一边割文的怎么办?
  那就再造一条val[文][AB]/2+val[理][AB]/2的边(图中双杠的边,把它也割掉)。反之亦然
这里写图片描述

  最后成了这样。。
这里写图片描述

  其实呢,这个图还是可以化简的(从黄学长那里看来的,简洁,cool~~~~~~~~~~)
这里写图片描述

  完结,撒花~~~
【代码】

#include<bits/stdc++.h>

#define imax(a,b) ((a>b)?(a):(b))
#define imin(a,b) ((a<b)?(a):(b))

using namespace std;

typedef long long ll;

const int N=120;
const int M=300050;
int S,T,sum,n,m;
int P[N][N];
int ne[M],to[M],val[M],h[M],tt;
int d[N][N][2],g[N][N][2],s[N][N][2];
int q[M],bfstime;
int vis[M],lev[M];

void read(int &x)
{
    x=0; char ch=getchar(); int f=1;
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(; isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
    x*=f;
}

void addedge(int a,int b,int c)
{ to[++tt]=b; ne[tt]=h[a]; h[a]=tt; val[tt]=c; }

void addgo(int a,int b,int c)
{ addedge(a,b,c); addedge(b,a,0); }

bool bfs()
{
    int head=1,tail=1;
    q[head]=S;
    vis[S]=++bfstime;
    lev[S]=1;
    while(head<=tail)
    {
        for(int p=h[q[head]];p;p=ne[p])
        {
            if(!val[p]) continue;
            if(vis[to[p]]==bfstime) continue;
            q[++tail]=to[p];
            vis[to[p]]=bfstime;
            lev[to[p]]=lev[q[head]]+1;
        }
        head++;
    }
    return vis[T]==bfstime;
}

int dfs(int now,int maxf)
{
    int ret=0,t;
    if(now==T || !maxf ) return maxf;
    for(int p=h[now];p;p=ne[p])
    {
        if(!val[p] || lev[to[p]]!=lev[now]+1) continue;
        t=dfs(to[p],imin(maxf,val[p]));
        val[p]-=t;
        val[p^1]+=t;
        maxf-=t;
        ret+=t;
    }
    if(maxf) lev[now]=-1;
    return ret;
}

int dinic()
{
    int ret=0;
    while(bfs()) ret+=dfs(S,1e9);
    return ret;
}

void init()
{
    read(n); read(m); tt=1;
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j) P[i][j]=(i-1)*m+j+1;
    S=P[n][m]+1; T=S+1;
    for(int o=0;o<2;o++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) read(d[i][j][o]),sum+=d[i][j][o],d[i][j][o]<<=1;

    for(int o=0;o<2;o++)
    for(int i=1;i<n;i++)
    for(int j=1;j<=m;j++) read(g[i][j][o]),sum+=g[i][j][o];

    for(int o=0;o<2;o++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<m;j++) read(s[i][j][o]),sum+=s[i][j][o];

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        addgo(S,P[i][j],d[i][j][0]+g[i][j][0]+g[i-1][j][0]+s[i][j][0]+s[i][j-1][0]);
        addgo(P[i][j],T,d[i][j][1]+g[i][j][1]+g[i-1][j][1]+s[i][j][1]+s[i][j-1][1]);

        if(i!=n)
        {
            addgo(P[i][j],P[i+1][j],g[i][j][0]+g[i][j][1]);
            addgo(P[i+1][j],P[i][j],g[i][j][0]+g[i][j][1]);
        }

        if(j!=m)
        {
            addgo(P[i][j],P[i][j+1],s[i][j][0]+s[i][j][1]);
            addgo(P[i][j+1],P[i][j],s[i][j][0]+s[i][j][1]);
        }
    }
}

int main()
{
    init();
    printf("%d\n",sum-(dinic()>>1));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值