正题
题目链接:https://www.luogu.com.cn/problem/P4313
题目大意
有n∗mn*mn∗m个人,第(i,j)(i,j)(i,j)选择文科就可以获得arti,jart_{i,j}arti,j的价值,选择理科就可以获得scii,jsci_{i,j}scii,j的价值。如果一个选择文科的人周围都选择了文科,那么就可以多获得same_arti,jsame\_art_{i,j}same_arti,j的价值。如果一个选择了理科的人周围都选择了理科,那么就可以多获得same_scii,jsame\_sci_{i,j}same_scii,j的价值。
求最大价值和。
解题思路
显然不考虑samesamesame的话如何将其模型转移到网络流上,考虑最小割。我们对于每个同学(i,j)(i,j)(i,j)建立一个节点pi,j,0p_{i,j,0}pi,j,0,然后S−>pi,j,0S->p_{i,j,0}S−>pi,j,0流量为arti,jart_{i,j}arti,j,pi,j,0−>Tp_{i,j,0}->Tpi,j,0−>T流量为scii,jsci_{i,j}scii,j。
考虑如何加入samesamesame入这个模型中,先考虑文科的,对于一个点我们发现如果它周围的都割掉了理科的边就不需要割same_artsame\_artsame_art,也就是我们需要新建一个节点连接这些周围的节点,这样这些被连接的节点就必须要割掉连向TTT的边 。显然我们还需要建立S−>pi,j,1S->p_{i,j,1}S−>pi,j,1流量为same_arti,jsame\_art_{i,j}same_arti,j
same_scii,jsame\_ sci_{i,j}same_scii,j同理。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define p(x,y,z) ((((x)-1)*m+(y))+(z)*S)
using namespace std;
const int N=3e4+10,inf=2147483647/3;
struct node{int to,next,w;
}a[N*20];
const int dx[5]={1,-1,0,0,0},dy[5]={0,0,1,-1,0};
int n,m,s,t,S,tot=1;
int dep[N],ls[N],ans;
queue<int> q;
void adde(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;a[tot].w=0;return;
}
bool bfs(){while(!q.empty())q.pop();memset(dep,0,sizeof(dep));dep[s]=1;q.push(s);while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(!a[i].w||dep[y])continue;dep[y]=dep[x]+1;if(y==t)return 1;q.push(y);}}return 0;
}
int dinic(int x,int flow){int rest=0,k;if(x==t)return flow;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(!a[i].w||dep[y]!=dep[x]+1)continue;rest+=(k=dinic(y,min(flow-rest,a[i].w)));a[i].w-=k;a[i^1].w+=k;if(rest==flow)return flow;}if(!rest)dep[x]=0;return rest;
}
int main()
{scanf("%d%d",&n,&m);S=n*m;s=p(n,m,2)+1;t=s+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int x;scanf("%d",&x);adde(s,p(i,j,0),x);ans+=x;}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int x;scanf("%d",&x);adde(p(i,j,0),t,x);ans+=x;}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int x;scanf("%d",&x);adde(s,p(i,j,1),x);ans+=x;for(int k=0;k<5;k++){int zx=i+dx[k],zy=j+dy[k];if(zx<1||zy<1||zx>n||zy>m)continue;adde(p(i,j,1),p(zx,zy,0),inf);}}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){int x;scanf("%d",&x);adde(p(i,j,2),t,x);ans+=x;for(int k=0;k<5;k++){int zx=i+dx[k],zy=j+dy[k];if(zx<1||zy<1||zx>n||zy>m)continue;adde(p(zx,zy,0),p(i,j,2),inf);}}while(bfs())ans-=dinic(s,inf);printf("%d",ans);
}