题目大意:有一个$n\times m$的矩阵,矩阵的每个位置上有一个同学,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。求一个方案,使得全班的喜悦值总和最大。
数据范围:$n,m≤100$,权值$≤5000$。
此题直接最小割,对于一对相邻的同学$(A,B)$,设$L_A$表示$A$同学选理的喜悦值,$W_A$表示$A$同学选文的喜悦值(前面说的两种情况$B$同理),设$L$为两个人都选理的额外收获喜悦值,$W$为两个人都选文的额外收获喜悦值,则有:
$S->A$ ,权值为:$L_A+\frac{1}{2}L$
$A->T$ ,权值为:$W_A+\frac{1}{2}W$
$S->A$ ,权值为:$L_A+\frac{1}{2}L$
$A->T$ ,权值为:$W_A+\frac{1}{2}W$
$A<->B$,权值为:$\frac{1}{2}(L+W)$
当点数变多的时候,情况会有所不同,基于这个思路简单变形下就好了
建完图后跑最大流就行了
B站上的数据比较水,我们OJ上的数据加强了必须加上一个特殊的剪枝才能过(新姿势get)
#include<bits/stdc++.h>
#define M 300005
#define N 105
#define INF 1145141919
#define F(_x,_y) for(int i=1;i<=(_x);i++) for(int j=1;j<=(_y);j++)
using namespace std; struct edge{int u,v,next;}e[M]={}; int head[M]={},use=;
void add(int x,int y,int z){e[use].u=y;e[use].v=z;e[use].next=head[x];head[x]=use++;}
void Add(int x,int y,int z){add(x,y,z); add(y,x,);}
int n,m,a[N][N]={},b[N][N]={},sum=,id[N][N]={},d[N][N]={},r[N][N]={},cnt=;
int S,T,vis[M]={}; queue<int> q; bool bfs(){
q.push(S); memset(vis,,(cnt+)<<); vis[S]=;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];~i;i=e[i].next)
if(e[i].v&&vis[e[i].u]==){
vis[e[i].u]=vis[u]+;
q.push(e[i].u);
}
}
return vis[T];
}
int dfs(int x,int flow){
if(x==T) return flow; int sum=;
for(int i=head[x];~i;i=e[i].next)
if(e[i].v&&vis[e[i].u]==vis[x]+){
int k=dfs(e[i].u,min(flow,e[i].v));
e[i].v-=k; e[i^].v+=k;
sum+=k; flow-=k;
if(flow==) return sum;
}
if(sum==) vis[x]=-;
return sum;
}
int dinic(){
int res=;
while(bfs())
res+=dfs(S,INF);
return res;
} int main(){
//freopen("in.txt","r",stdin);
memset(head,-,sizeof(head));
S=; T=; cnt=;
scanf("%d%d",&n,&m);
F(n,m) id[i][j]=++cnt;
F(n,m) scanf("%d",&a[i][j]),sum+=a[i][j],a[i][j]<<=;
F(n,m) scanf("%d",&b[i][j]),sum+=b[i][j],b[i][j]<<=;
F(n-,m){
int x; scanf("%d",&x);
a[i][j]+=x; a[i+][j]+=x; sum+=x; d[i][j]+=x;
}
F(n-,m){
int x; scanf("%d",&x);
b[i][j]+=x; b[i+][j]+=x; sum+=x; d[i][j]+=x;
}
F(n,m-){
int x; scanf("%d",&x);
a[i][j]+=x; a[i][j+]+=x; sum+=x; r[i][j]+=x;
}
F(n,m-){
int x; scanf("%d",&x);
b[i][j]+=x; b[i][j+]+=x; sum+=x; r[i][j]+=x;
}
F(n,m){
Add(S,id[i][j],a[i][j]);
Add(id[i][j],T,b[i][j]);
}
F(n,m-){
add(id[i][j],id[i][j+],r[i][j]);
add(id[i][j+],id[i][j],r[i][j]);
}
F(n-,m){
add(id[i][j],id[i+][j],d[i][j]);
add(id[i+][j],id[i][j],d[i][j]);
}
int hh=dinic();
hh>>=;
cout<<sum-hh<<endl;
}