bzoj3894: 文理分科

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3894

题解

  S向每个人连边,容量是选文科的满意值;每个点向T连边,容量是选理科的满意值。
  再新建2*N*M个点,表示每个人和相邻的人都选文(p1)、或都选理(p2)。S向p1连边,容量为这个人的same_art,p1再向这个人和相邻的四个人都连inf的边;p2向T连边,容量为这个人的same_science,这个人以及周围相邻的四个人向T2连边。
  跑最小割。

代码

//最小割
#include <cstdio>
#include <algorithm>
#define maxn 300000
#define inf 0x3f3f3f3f
using namespace std;
int head[maxn], to[maxn], c[maxn], nex[maxn], N, a[150][150], s[150][150], sa[150][150],
    ss[150][150], Exit, d[maxn], num[maxn], tot=1, S, T, last[maxn], M, ans;
void adde(int a, int b, int cc){to[++tot]=b;c[tot]=cc;nex[tot]=head[a];head[a]=tot;}
void adde2(int a, int b, int cc){adde(a,b,cc);adde(b,a,0);}
int isap(int pos, int in)
{
    if(pos==T)return in;
    int flow=0, t;
    for(int &p=last[pos];p;p=nex[p])
        if(c[p] and d[to[p]]+1==d[pos])
        {
            flow+= t=isap(to[p],min(c[p],in-flow));
            c[p]-=t, c[p^1]+=t;
            if(Exit or in==flow)return flow;
        }
    Exit=--num[d[pos]]==0;
    ++num[++d[pos]];
    last[pos]=head[pos];
    return flow;
}
int tab(int a, int b){return (a-1)*M+b;}
void init()
{
    int i, j, t1, t2;
    scanf("%d%d",&N,&M);
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",a[i]+j);
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",s[i]+j);
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",sa[i]+j);
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)scanf("%d",ss[i]+j);
    S=N*M*3+10, T=S+1;
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)
    {
        adde2(S,tab(i,j),a[i][j]);
        adde2(tab(i,j),T,s[i][j]);
        t1=N*M+tab(i,j), t2=N*M+N*M+tab(i,j);
        adde2(S,t1,sa[i][j]), adde2(t2,T,ss[i][j]);
        adde2(t1,tab(i,j),inf), adde2(tab(i,j),t2,inf);
        if(i>1)adde2(t1,tab(i-1,j),inf),adde2(tab(i-1,j),t2,inf);
        if(i<N)adde2(t1,tab(i+1,j),inf),adde2(tab(i+1,j),t2,inf);
        if(j>1)adde2(t1,tab(i,j-1),inf),adde2(tab(i,j-1),t2,inf);
        if(j<M)adde2(t1,tab(i,j+1),inf),adde2(tab(i,j+1),t2,inf);
    }
    for(i=1;i<=N;i++)for(j=1;j<=M;j++)ans+=a[i][j]+s[i][j]+sa[i][j]+ss[i][j];
}
int main()
{
    init();
    while(!Exit)ans-=isap(S,inf);
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值