BZOJ2127:happiness(最小割)

题面
题意:一群人,每人选文选里都有收益,相邻两个人同时选文和选理也有收益
问最大收益

二选一,应该是网络流
网络流忘得差不多了
我刚看到的时候什么都想不出

二选一能处理的问题
①两个人选不同的有代价
②一群人选相同某个的收益(一群人中的某人不选有代价)

属于S集则选文,否则选理

只考虑两个人A,B
套①,看似无法处理共同的问题
可以把共同选文的收益平分加在与S相连的边上
共同选理的收益加在与T相连的边上

算出代价为(共同选文的收益+共同选理的收益)/2
根据套路,直接在A,B间连双向边

套②,就很显然了
有一人选了理就无法获得共同选文的收益
在图上:有一人属于T集,则要割一条连接S与A,B的边

新建一个点与A,B连通,且与S有一条容量为共同选文收益的边
选理也一样
这里写图片描述

一丢丢总结

对于某个点(除了拆点),永远与S和T分别有边
且这两条割且只割一条,决定了它选什么
除了上面两个问题,其他都是最小割不可做的(同选有代价,异选有收益)

网络流边从2开始标号
网络流边从2开始标号
网络流边从2开始标号
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=12345,M=2002000,oo=1e9+7;

int n,m,ans,T;
int wen[110][110],li[110][110],a[110][110];
int to[M],nex[M],cap[M],cnt=2;
int head[N],lter[N],level[N],q[N];

int p(int x,int y)
{
    return (x-1)*m+y;
}
void add(int u,int v,int w,int rw)
{
    to[cnt]=v;
    cap[cnt]=w;
    nex[cnt]=head[u];
    head[u]=cnt++;
    to[cnt]=u;
    cap[cnt]=rw;
    nex[cnt]=head[v];
    head[v]=cnt++;
}

bool bfs()
{
    q[1]=0;
    mmcp(lter,head);
    mmst(level,-1);
    level[0]=1;
    int hh=1,tt=1;
    for(;hh<=tt;)
    {
        int hy=q[hh++];
        for(int h=head[hy];h;h=nex[h])
        if(cap[h]&&level[to[h]]<0)
        {
            q[++tt]=to[h];
            level[to[h]]=level[hy]+1;
        }
    }
    return level[T]>0;
}

int dfs(int v,int f)
{
    if(v==T||!f)
    return f;
    int ret=0;
    for(int &h=lter[v];h;h=nex[h])
    if(cap[h]&&level[to[h]]>level[v])
    {
        int d=dfs(to[h],min(f,cap[h]));
        ret+=d;
        cap[h]-=d;
        cap[h^1]+=d;
        f-=d;
        if(!f)
        break;
    }
    return ret;
}

int main()
{
    cin>>n>>m;
    T=n*m+1;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&wen[i][j]),wen[i][j]<<=1,ans+=wen[i][j];

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    scanf("%d",&li[i][j]),li[i][j]<<=1,ans+=li[i][j];

    for(int i=1;i<=n-1;i++)
    for(int j=1;j<=m;j++)
    {
        scanf("%d",&a[i][j]);
        ans+=a[i][j]+a[i][j];
        wen[i][j]+=a[i][j];
        wen[i+1][j]+=a[i][j];
    }

    for(int i=1;i<=n-1;i++)
    for(int j=1;j<=m;j++)
    {
        int x;
        scanf("%d",&x);
        ans+=x+x;
        add(p(i,j),p(i+1,j),a[i][j]+x,a[i][j]+x);
        li[i][j]+=x;
        li[i+1][j]+=x;
    }

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m-1;j++)
    {
        scanf("%d",&a[i][j]);
        ans+=a[i][j]+a[i][j];
        wen[i][j]+=a[i][j];
        wen[i][j+1]+=a[i][j];
    }

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m-1;j++)
    {
        int x;
        scanf("%d",&x);
        ans+=x+x;
        add(p(i,j),p(i,j+1),a[i][j]+x,a[i][j]+x);
        li[i][j]+=x;
        li[i][j+1]+=x;
    }

    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
    {
        add(0,p(i,j),wen[i][j],0);
        add(p(i,j),T,li[i][j],0);
    }

    int flow=0;
    while(bfs())
    ans-=dfs(0,oo);

    cout<<ans/2<<endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值