bzoj3894 文理分科(网络流最小割)

Description
文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)
小P所在的班级要进行文理分科。他的班级可以用一个n*m的矩阵进行描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择一科。同学们在选择科目的时候会获得一个满意值。满意值按如下的方式得到:
1.如果第i行第秒J的同学选择了文科,则他将获得art[i][j]的满意值,如果选择理科,将得到science[i][j]的满意值。
2.如果第i行第J列的同学选择了文科,并且他相邻(两个格子相邻当且仅当它们拥有一条相同的边)的同学全部选择了文科,则他会更开心,所以会增加same_art[i][j]的满意值。
3.如果第i行第j列的同学选择了理科,并且他相邻的同学全部选择了理科,则增加same_science[i]j[]的满意值。小P想知道,大家应该如何选择,才能使所有人的满意值之和最大。请告诉他这个最大值。

分析:
1.对于这种n选1可以得到一些收益,并且在一些特殊情况下有更多收益的,一般判断为网络流。而处理冲突情况,一般通过添加一条inf边来表示。
这个题目直接正向求不好求,于是我们选择将总收益减去最小代价。
2.那么我们令S为学文,T为学理,对于额外的收入,加入一个新的点。
这里写图片描述
3.而具体的来说,对于这个图,x表示一个学生,与S连通表示学文,与T连通表示学理。y表示新加的点,用来计算同时学理的额外收益,与S连通表示不
要额外收益,与T连通表示要额外收益先算出总收益,在用最小割减去即可。
4.于是我们可以列出方程:

c+d=B[i]+WB(学生i学文,不要额外收益,损失学理收益,损失额外收益)
a+b=A[i](学生i学理,要额外收益,损失学文收益)
b+f+c=inf(学生i学文,要额外收益)
a+e+d=A[i]+WB(学生学理,不要额外收益,损失学文收益,损失学理额外收益)

解得:

a=A[i]
b=0
c=B[i]
d=WB
e=0
f=inf

同时学文类似,只是方向不同,收益不同。

5.所以,最终的建图就是:

step 1 S向每一个学生连边,容量为学文的收益,每个学生再向T连边,容量为学理的收益。
step 2 S向代表相邻5个人都学文收益点连边,容量为同时学文的收益
step 3 该点再向对应5个人连边,容量为inf,表示只要有人学理,就必须割掉同时学文的收益
step 4 学理类似
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxm=1000010;
const int maxn=300010;
const int INF=1e9;
int to[maxm],Next[maxm],Begin[maxn],w[maxm],e;
int n,m;
int art[110][110],sci[110][110],same_art[110][110],same_sci[110][110];
int id[110][110],gap[maxn],d[maxn];
int dx[5]={0,0,0,1,-1},dy[5]={0,1,-1,0,0};
int s,t;
int sum;
void addedge(int x,int y,int z){
    to[++e]=y;
    Next[e]=Begin[x];
    Begin[x]=e;
    w[e]=z;
}
void add(int x,int y,int z){
    addedge(x,y,z);addedge(y,x,0);
}
int isap(int x,int flow){
    if(x==t) return flow;
    int res=flow;
    int v;
    for(int i=Begin[x];i;i=Next[i])if(w[i]>0 && d[x]==d[v=to[i]]+1){
        int Min=isap(v,min(res,w[i]));
        w[i]-=Min;w[i^1]+=Min;
        res-=Min;
        if(!res) return flow;
    }
    if(!(--gap[d[x]])) d[s]=t;
    ++gap[++d[x]];
    return flow-res;
}
int cnt;
bool pd(int x,int y){
    return x>=1 && x<=n && y>=1 && y<=m;
}
int main(){
    scanf("%d%d",&n,&m);
    e=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) id[i][j]=++cnt;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&art[i][j]),sum+=art[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&sci[i][j]),sum+=sci[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&same_art[i][j]),sum+=same_art[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&same_sci[i][j]),sum+=same_sci[i][j];
    s=cnt*3+1;t=s+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            int tmp=(id[i][j]-1)*3;
            add(s,tmp+1,art[i][j]);
            add(tmp+1,t,sci[i][j]);
            add(s,tmp+2,same_art[i][j]);
            for(int k=0;k<5;k++){
                int tx=i+dx[k],ty=j+dy[k];
                if(!pd(tx,ty)) continue;
                add(tmp+2,3*(id[tx][ty]-1)+1,INF);
            }
            add(tmp+3,t,same_sci[i][j]);
            for(int k=0;k<5;k++){
                int tx=i+dx[k],ty=j+dy[k];
                if(!pd(tx,ty)) continue;
                add(3*(id[tx][ty]-1)+1,tmp+3,INF);
            }
        }
    int maxflow=0;
    for(gap[0]=t;d[s]<t;){
        maxflow+=isap(s,INF);
    }
    printf("%d",sum-maxflow);
    return 0;
}

^_^

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值