【NOI2015模拟9.9】文理分科

10 篇文章 0 订阅
4 篇文章 0 订阅

Description

这里写图片描述

Input

这里写图片描述

Output

一行表示最大的满意值

Sample Input

3 4
13 2 4 13
7 13 8 12
18 17 0 5
8 13 15 4
11 3 8 11
11 18 6 5
1 2 3 4
4 2 3 2
3 1 0 4
3 2 3 2
0 2 2 1
0 2 4 4

Sample Output

152

Solution

二元关系的裸题,就是限制略微有点不一样
没有学过二元关系的建议先看看某些大神关于二元关系的介绍
也可以看看我的另一篇博客,点这里进入(当然没有大神写的好)
这次的限制是周围的人全部要选同样的
那么可以对于每个点,多创建两个点,分别表示这个点的周围全部选了文科或全部选了理科,分别表示为x’和x”
构建最小割模型,连边方式如下:
点x归为S集表示选理科,T集表示选文科
点x’或x”归为S集表示不是真的,T集表示为真
源点向所有点连容量为理科的喜悦值
所有点向汇点连容量为文科的喜悦值

暂时只考虑x’即文科全选
源点向x’连0
x’向汇点连x的附加值
x向x’连x的附加值
为什么是对的呢?
假设x选了理科,即割了x->T的边
那么x’->T的边也一定会被割,否则就不是割了,x’便归为S,也就是不满足全选文科,不矛盾
假如x选了文科,即割了S->x的边
那么S->x’的边也会被割,但容量为0,同时x’归为了T,也就是满足全选文科,不矛盾

接着为了保证条件,所有与x’相邻的点y向x连正无穷,即如果y归为S即选理科,那么x’也一定归为S即不满足全选文科

理科反过来就行了

答案就是输入的所有数减最小割

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 10100000
#define INF 214748347
#define po(i,j) ((i-1)*m+j)
using namespace std;
int next[N],last[N],data[N],to[N],S,T,ans,d[N],bz[N],tot=1;
void putin(int x,int y,int z)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;
    next[++tot]=last[y];last[y]=tot;to[tot]=x;data[tot]=0;
}
bool bfs()
{
    int i=0,j=1;
    memset(bz,0,sizeof(bz));
    bz[d[1]=S]=1;
    while(i<j)
    {
        int x=d[++i];
        for(int k=last[x];k;k=next[k]) if(bz[to[k]]==0&&data[k]>0) bz[to[k]]=bz[x]+1,d[++j]=to[k];
    }
    return bz[T]>0;
}
int dfs(int x,int t)
{
    if(x==T) return t;
    int ans=0;
    for(int i=last[x];i;i=next[i])
    if(bz[to[i]]==bz[x]+1&&data[i])
    {
        int y=to[i],jy=0;
        jy=dfs(y,min(t,data[i]));
        if(jy) data[i]-=jy,data[i^1]+=jy,ans+=jy,t-=jy;
        if(t==0) return ans;
    }
    if(ans==0) bz[x]=-1;
    return ans;
}
int main()
{
    int n,m;scanf("%d%d",&n,&m);S=n*m*3+1;T=n*m*3+2;
    fo(i,1,n) fo(j,1,m)
    {
        int x;scanf("%d",&x);putin(po(i,j),T,x);ans+=x;
    }
    fo(i,1,n) fo(j,1,m)
    {
        int x;scanf("%d",&x);putin(S,po(i,j),x);ans+=x;
        if(i!=1) putin(po(i,j),po(i-1,j)+n*m,INF),putin(po(i-1,j)+2*n*m,po(i,j),INF);
        if(j!=1) putin(po(i,j),po(i,j-1)+n*m,INF),putin(po(i,j-1)+2*n*m,po(i,j),INF);
        if(i!=n) putin(po(i,j),po(i+1,j)+n*m,INF),putin(po(i+1,j)+2*n*m,po(i,j),INF);
        if(j!=m) putin(po(i,j),po(i,j+1)+n*m,INF),putin(po(i,j+1)+2*n*m,po(i,j),INF);
    }
    fo(i,1,n) fo(j,1,m)
    {
        int x;scanf("%d",&x);ans+=x;
        putin(po(i,j),n*m+po(i,j),x);
        putin(n*m+po(i,j),T,x);
        putin(S,n*m+po(i,j),0);
    }
    fo(i,1,n) fo(j,1,m)
    {
        int x;scanf("%d",&x);ans+=x;
        putin(2*n*m+po(i,j),po(i,j),x);
        putin(2*n*m+po(i,j),T,0);
        putin(S,2*n*m+po(i,j),x);
    }
    while(bfs()) ans-=dfs(S,INF);
    printf("%d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值