BZOJ 3894 网络流最小割 解题报告

本文探讨了一个关于文理分科的选择问题,通过建立一个数学模型,利用图论中的最小割算法来求解如何让班级内学生在文理分科选择上达到满意的最大化。介绍了具体的算法实现细节及代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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想知道,大家应该如何选择,才能使所有人的满意值之和最大。请告诉他这个最大值。

Input

第一行为两个正整数:n,m
接下来n术m个整数,表示art[i][j];
接下来n术m个整数.表示science[i][j];
接下来n术m个整数,表示same_art[i][j];

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

【解题报告】
(原谅我没有买权限号,我也不知道我能不能A。。。)
令S集为学文,T集为学理。
每个人学文或者学理的满意度很好连边。
如果某个集合内的人都学理会获得一个满意度,那么就新加一个点,将集合内的所有人向这个点连流量为正无穷的边,再从这个点向T连一条流量为满意度的边,表示集合内任意一个人学文都要把这个点与T的边割掉。
都学文同理。
建完图之后跑最小割即可。
这里写图片描述
自己画了个图,应该会更形象些。
大神题解http://blog.youkuaiyun.com/PoPoQQQ/article/details/43968017

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=3e4+10;
const int M=1e6+5;
const int inf=1e8;

const int dx[5]={0,1,-1,0,0};
const int dy[5]={0,0,0,1,-1};

struct edge 
{
    int next,to,f;
    edge(){}
    edge(int _n,int _t,int _f):next(_n),to(_t),f(_f){}
}e[M];
int n,m,S,T,ans;
int w[105][105],cnt_p;
int first[N],tot=1;
int q[N],d[N];

inline void Add_Edges(int x,int y,int f) 
{
    e[++tot]=edge(first[x],y,f),first[x]=tot;
    e[++tot]=edge(first[y],x,0),first[y]=tot;
}
bool bfs() 
{
    int l,r,x;
    memset(d,-1,sizeof(d));
    d[q[1]=S]=1;
    for(l=r=1;l!=r+1;++l)
    for(x=first[q[l]];x;x=e[x].next)
        if(!~d[e[x].to]&&e[x].f) 
        {
            d[q[++r] = e[x].to] = d[q[l]] + 1;
            if(e[x].to == T) return 1;
        }
    return 0;
}
int dfs(int p,int lim) 
{
    if (p==T||!lim) return lim;
    int x,tmp,rest=lim;
    for(x=first[p];x&&rest;x=e[x].next) 
    if(d[e[x].to]==d[p]+1&&((tmp=min(e[x].f,rest))>0)) 
    {
        rest-=(tmp=dfs(e[x].to,tmp));
        e[x].f-=tmp,e[x^1].f+=tmp;
        if(!rest) return lim;
    }
    if(rest) d[p]=-1;
    return lim-rest;
}
inline int Dinic() 
{
    int res=0;
    while(bfs())
    res+=dfs(S, inf);
    return res;
}
inline bool in(int x, int y) 
{
    return x&&y&&x<=n&&y<=m;
}
#define X i+dx[k]
#define Y j+dy[k]
#define p1(i,j) w[i][j]*3
#define p2(i,j) w[i][j]*3+1
#define p3(i,j) w[i][j]*3+2
int main() 
{
    int i,j,k,x;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i)
    for (j=1;j<=m;++j)
        w[i][j]=++cnt_p;
    S =n*m*3+3,T=S+1;
    for(i=1;i<=n;++i)
    for(j=1;j<=m;++j)
    {
        scanf("%d",&x);
        Add_Edges(S,p1(i,j),x),ans+=x;  
    }
    for(i=1;i<=n;++i)
    for(j=1;j<=m;++j)
    {
        scanf("%d",&x);
        Add_Edges(p1(i,j),T,x),ans+=x;
    }
    for(i=1;i<=n;++i)
    for(j=1;j<=m;++j) 
    {
        scanf("%d",&x);
        Add_Edges(S,p2(i,j),x), ans+=x;
        for(k=0;k<=4;++k)
            if(in(X,Y)) Add_Edges(p2(i,j),p1(X,Y),inf);
    }
    for(i=1;i<=n;++i)
    for(j=1;j<=m;++j) 
    {
        scanf("%d",&x);
        Add_Edges(p3(i,j),T,x),ans+=x;
        for(k=0;k<=4;++k)
            if(in(X,Y)) Add_Edges(p1(X,Y),p3(i,j),inf);
    }
    printf("%d\n",ans-Dinic());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值