LuoguP4313 BZOJ3894 文理分科——最小割

洛谷:文理分科 传送门

题目描述:

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

输入输出样例

输入样例:

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 

输出样例:

152

这道题看了很久,在错(D)误(P)的道路上一路狂飙。试了好几种状态,每种状态想了好几种状态转移方程,就是找不到能够证明正确性的解法。厚着脸皮去问大佬,大佬回答:这难道不是傻逼最小割吗?

听了之后我恍然大悟,立马(花了1个小时)找到了正确的模型和正确的建边方式。

把每个人当做一个点那么选文或者选理科就是两个与众不同的集合

扫描二维码关注公众号,回复: 3312400 查看本文章

首先只考虑前两种情况,即对于每个人只选文科或者选理科的情况。
从源点向每个点连一条边,边权为art,从汇点向每个点连一条边,边权为science。单看这个图直接跑网络流会产生最小割。如果一条边被割断了就表示不选择这个情况。
也就是说:
若源点连出的边被割断了,则表示我不选择文科。同理,若连到汇点的边被割断了就表示不选择理科。


那么前两种情况就处理完了。剩下的就是处理周围4个二货同学的事情了。
对于一个点周围的几个相邻的点的情况,我们新建一个点,将S连向这个点,长度为同时选文可获得的收益,如果该边被割,则说明这些人不同时选文,不能获得同时选文可获得的收益。理科的情况以此类推。
那么怎么保证如果这几个人选相同的科目时能够不割断这个边呢?
从周围这个点向周围的同学连一条INF的边,那么这种情况下都选同一科时,新边肯定不会被割断,就保证了网络流当中的割是使剩下的图满足条件的最小值。
理科的建图就是相反的(从周围的同学连向自己,具体理科建图方式请看代码)。


这个时候网络流中的割就是舍去之后满足条件的需要舍去的值(读着有点拗口)。
根据最大流最小割定理。求出网络流图中的最大流,就是最小割。
那么用总的开心值减去最小割就是最大流了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#define num(i,j) (((i)-1)*m+(j))
using namespace std;
const int maxn=2000010-3;
struct edge
{
    int v,w,next,fxb;
}e[maxn*10];
int pre[maxn],head[maxn],dis[maxn],num=1,S=1,T;
inline void add_Edge(int u,int v,int w)
{
    e[num].v=v;e[num].w=w;e[num].next=head[u];e[num].fxb=num+1;head[u]=num;num++;
    e[num].v=u;e[num].w=0;e[num].next=head[v];e[num].fxb=num-1;head[v]=num;num++;
}
inline bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue<int> q;
    dis[S]=1;
    q.push(S);
    while(!q.empty())
    {
	int u=q.front();
    	q.pop();
    	for(register int i=head[u];i;i=e[i].next)
    	{
      	    int v=e[i].v;
      	    if(dis[v]==-1&&e[i].w>0)
      	    {
       		dis[v]=dis[u]+1;
        	q.push(v);
	    }
	}
    }
    if(dis[T]==-1)
        return false;
    else
        return true;
}
inline int dfs(int x,int f)
{
    int temp,total=0;
    if(x==T) return f;
    for(register int i=pre[x];i;i=e[i].next)
    {
        int v=e[i].v;
        if(dis[v]!=dis[x]+1) continue;
        if(e[i].w<=0)continue;
        temp=dfs(v,min(f, e[i].w));
        total+=temp;f-=temp;
        e[i].w-=temp;e[e[i].fxb].w+=temp;pre[x]=i;
        if(!f)return total;
    }
    return total;
}
#define MAT 510
int c1[4]={0,0,1,-1};
int c2[4]={1,-1,0,0};
int n,m;
inline bool check(int x,int y)
{
    return x>0&&x<n&&y<m&&y>1;
}
int main()
{
    int sum=0,newNode,sa,id;
    scanf("%d%d",&n,&m);
    S=n*m+1;T=n*m+2,newNode=n*m+2;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
	{
            scanf("%d",&sa);
	    id=num(i,j);
            sum+=sa;
            add_Edge(S,id,sa);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
	{
            scanf("%d",&sa);
	    id=num(i,j);
            sum+=sa;
            add_Edge(id,T,sa);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
	{
	    scanf("%d",&sa);
	    id=num(i,j);
	    newNode++;
            sum+=sa;
            add_Edge(S,newNode,sa);
            add_Edge(newNode,id,0x3f3f3f3f);
            for(int k=0;k<4;k++)
                if(i+c1[k]>=1&&i+c1[k]<=n&&j+c2[k]>=1&&j+c2[k]<=m)
                    add_Edge(newNode,num(i+c1[k],j+c2[k]),0x3f3f3f3f);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
	{
            scanf("%d",&sa);
	    id=num(i,j);
	    newNode++;
            sum+=sa;
            add_Edge(newNode,T,sa);
            add_Edge(id,newNode,0x3f3f3f3f);
            for(int k=0;k<4;k++)
                if( i+c1[k]>=1&&i+c1[k]<=n&&j+c2[k]>=1&&j+c2[k]<=m)
                    add_Edge(num(i+c1[k],j+c2[k]),newNode,0x3f3f3f3f);
        }
    int f=0,mincut=0;
    while(bfs())
    {
	memcpy(pre, head, sizeof(head));
    	while(f=dfs(S,0x3f3f3f3f))mincut+=f;
    }
    printf("%d\n",sum-mincut);
    return 0;
}

这个网络流是我直接用的很久之前的板,毕竟很久都没有打过了

猜你喜欢

转载自blog.csdn.net/u014085852/article/details/81334388