BZOJ 3894: 文理分科【最小割限制收益冲突】

BZOJ3894

题目分析:

应该算是最小割的套路题吧。
先把所有收益统计到sum中,再看要损失掉哪些收益(使割掉的边最小)。

  • 每个点文理冲突,所以从 S S S ( i , j ) (i,j) (i,j)连容量为 a r t [ i ] [ j ] art[i][j] art[i][j]的边,从 ( i , j ) (i,j) (i,j) T T T连容量为 s c i e n c e [ i ] [ j ] science[i][j] science[i][j]的边。两条边必须割掉其中一条
  • s a m e _ a r t [ i ] [ j ] same\_art[i][j] same_art[i][j]与邻接点或者自己选理科的收益冲突,所以再建一个点 ( i , j ) ′ (i,j)' (i,j)。从 S S S ( i , j ) ′ (i,j)' (i,j)连容量为 s a m e _ a r t [ i ] [ j ] same\_art[i][j] same_art[i][j]的边,从 ( i , j ) ′ (i,j)' (i,j) ( i , j ) (i,j) (i,j)和邻接点连容量为 ∞ \infty 的边。如果 s a m e _ a r t [ i ] [ j ] same\_art[i][j] same_art[i][j]要保留,那么 ( i , j ) (i,j) (i,j)和邻接点选理科的边就一定会被割掉
  • s a m e _ s c i e n c e [ i ] [ j ] same\_science[i][j] same_science[i][j]与邻接点或者自己选文科的收益冲突,所以再建一个点 ( i , j ) ′ ′ (i,j)'' (i,j)。从 ( i , j ) ′ ′ (i,j)'' (i,j) T T T连容量为 s a m e _ s c i e n c e [ i ] [ j ] same\_science[i][j] same_science[i][j]的边,从 ( i , j ) (i,j) (i,j)和邻接点向 ( i , j ) ′ ′ (i,j)'' (i,j)连容量为 ∞ \infty 的边。如果 s a m e _ s c i e n c e [ i ] [ j ] same\_science[i][j] same_science[i][j]要保留,那么 ( i , j ) (i,j) (i,j)和邻接点选文科的边就一定会被割掉。

然后跑一个最小割求出最少的损失收益,用sum减去就是答案
注意不要把第二和第三种情况混在一起了,因为边是有方向的。
总点数应该是 3 n m + 2 3nm+2 3nm+2

Code:

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define maxn 30005
#define maxm 300005
#define LL long long
using namespace std;
template<class T>inline void read(T &a){
    char c;while(!isdigit(c=getchar()));
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
const int inf = 0x3f3f3f3f;
int n,m,S,T;
int fir[maxn],cur[maxn],nxt[maxm],to[maxm],tot=1,cap[maxm];
inline void line(int x,int y,LL z){
    nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,cap[tot]=z;
    nxt[++tot]=fir[y],fir[y]=tot,to[tot]=x,cap[tot]=0;
}
int dis[maxn];
queue<int>q;
bool bfs()
{
    memset(dis,0,(T+1)<<2);
    dis[T]=1,q.push(T);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=fir[u];i;i=nxt[i]) if(cap[i^1]&&!dis[to[i]]){
            dis[to[i]]=dis[u]+1;
            q.push(to[i]);
        }
    }
    return dis[S];
}
int dfs(int u,int lim)
{
    if(u==T) return lim;
    int need=lim,delta;
    for(int &i=cur[u];i;i=nxt[i]) if(cap[i]&&dis[u]==dis[to[i]]+1){
        delta=dfs(to[i],min(cap[i],need));
        cap[i]-=delta,cap[i^1]+=delta;
        if(!(need-=delta)) break;
    }
    return lim-need;
}
int Dinic(){
    int flow=0;
    while(bfs()) memcpy(cur,fir,(T+1)<<2),flow+=dfs(S,inf);
    return flow;
}
int p[105][105];
int dx[5]={1,-1,0,0,0};
int dy[5]={0,0,1,-1,0};
int main()
{
	int x,y,z,sum=0;
    read(n),read(m);S=0,T=n*m*3+1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			read(x),line(S,p[i][j]=(i-1)*m+j,x),sum+=x;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			read(x),line(p[i][j],T,x),sum+=x;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			read(z),sum+=z;
			for(int k=0;k<5;k++){
				x=i+dx[k],y=j+dy[k];
				if(p[x][y]) line(p[i][j]+n*m,p[x][y],inf);
			}
			line(S,p[i][j]+n*m,z);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			read(z),sum+=z;
			for(int k=0;k<5;k++){
				x=i+dx[k],y=j+dy[k];
				if(p[x][y]) line(p[x][y],p[i][j]+2*n*m,inf);
			}
			line(p[i][j]+2*n*m,T,z);
		}
	printf("%d",sum-Dinic());
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值