zoukankan      html  css  js  c++  java
  • P4313 文理分科

    这篇博客是文理分科的题解暨最大流建模思路的套路整理。

    很多人看到最大流的题就说是最小割板子。那最小割到底有什么意义?我被这个问题困惑了很久。

    我们到底要去割一个怎样的图?我们为什么要去割这个图?

    做了一些习题之后,我大概发现了如下的规律:

    源点和汇点表示两个矛盾的状态,最小割的过程即是找使状态合法的最小代价。

    形象化地,我们以本题为例。

    显然题中的人不可能既选文科又选理科(不排除现实生活中有这样的神仙)。

    则我们将最终和源点(S)相连的点认为是选择文科,理科同理。

    考虑如何连边:

    每个点向源点连一条价值为他选择文科的满意值,汇点同理。

    若连向源点的边被割断,则代表他选择理科,反之亦然。

    发现这样建图一个人的文科和理科总有一个会被割掉,符合题意。

    对于(same_{art}[i][j]),新建节点(cnt),考虑它的要求。

    若要满足全为文科,则所有理科的边都要割掉,否则我们也可以割掉这一条边。

    故连(S)(cnt)价值为(same_{art}[i][j])的边,再从(cnt)((i,j))和其相邻点连价值为(inf)的边。

    对于理科的(same_{science}[i][j])是一样的道理。

    我们发现将(S)(T)具象化以后,每一条连边都是有意义的,这样更有利于解题。

    代码如下,仅供参考(本人码风就那样,多多体谅):

    #include<bits/stdc++.h>
    using namespace std;
    #define inf 1e9
    const int N=105;
    const int maxn=3e5+10;
    int n,m,ans,t,cnt;
    int beg[maxn],nex[maxn],to[maxn],w[maxn],e;
    inline void add(int x,int y,int z){
    	nex[e]=beg[x];beg[x]=e;
    	to[e]=y;w[e]=z;e++;
    }
    int dep[maxn];queue<int>q;
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    	return x*f;
    }
    inline int id(int x,int y){return (x-1)*m+y;}
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    inline int bfs(){
    	memset(dep,0,sizeof(dep));
    	dep[0]=1;q.push(0);
    	while(!q.empty()){
    		int x=q.front();
    		q.pop();
    		for(int i=beg[x];~i;i=nex[i])
    			if(w[i]&&!dep[to[i]]){
    				dep[to[i]]=dep[x]+1;
    				q.push(to[i]);
    			}
    	}
    	return dep[t]!=0;
    }
    inline int dfs(int x,int lim){
    	if(x==t||!lim)return lim;
    	int res=0;
    	for(int i=beg[x];~i;i=nex[i])
    		if(w[i]&&dep[to[i]]==dep[x]+1){
    			int f=dfs(to[i],min(w[i],lim));
    			if(!f){dep[to[i]]=-1;continue;}
    			lim-=f;res+=f;
    			w[i]-=f;w[i^1]+=f;
    		}
    	return res;
    }
    int main(){
    	n=read(),m=read();
    	memset(beg,-1,sizeof(beg));
    	int val;t=cnt=n*m+1;
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			val=read();ans+=val;
    			add(0,id(i,j),val);add(id(i,j),0,0);
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			val=read();ans+=val;
    			add(id(i,j),t,val);add(t,id(i,j),0);
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			cnt++;val=read();ans+=val;
    			add(0,cnt,val);add(cnt,0,0);
    			add(cnt,id(i,j),inf);add(id(i,j),cnt,0);
    			for(int k=0;k<4;k++){
    				int x=i+dx[k];int y=j+dy[k];
    				if(x<1||y<1||x>n||y>m)continue;
    				add(cnt,id(x,y),inf);add(id(x,y),cnt,0);
    			}
    		}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++){
    			cnt++;val=read();ans+=val;
    			add(cnt,t,val);add(t,cnt,0);
    			add(id(i,j),cnt,inf);add(cnt,id(i,j),0);
    			for(int k=0;k<4;k++){
    				int x=i+dx[k];int y=j+dy[k];
    				if(x<1||y<1||x>n||y>m)continue;
    				add(id(x,y),cnt,inf);add(cnt,id(x,y),0);
    			}
    		}
    	while(bfs())ans-=dfs(0,inf);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    深深地感到自己的弱小。

  • 相关阅读:
    利用Python imaplib和email模块 读取邮件文本内容及附件内容
    sqlachemy 查询当日数据,
    【Flask】abort和errorhandler、app_errorhandler进行请求中断及自定义异常处理
    CentOS 7 安装与卸载MySQL 5.7
    解决python2.7 UnicodeDecodeError和UnicodeEncodeError问题
    python常用的十进制、16进制、字符串、字节串之间的转换
    Python任务调度模块 – APScheduler,Flask-APScheduler实现定时任务
    flask celery 使用方法
    Linux权限详解 命令之 chmod:修改权限
    MySQL中无GROUP BY直接HAVING的问题【转】
  • 原文地址:https://www.cnblogs.com/syzf2222/p/14027767.html
Copyright © 2011-2022 走看看