luogu P1646 [国家集训队]happiness(最小割)
题目大意
高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友。这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值。
作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大。
解题思路
和HDU6598很像,都是对点进行黑白染色,然后点之间有一些根据相对颜色产生的贡献,问如何产生最大的收益。
对此我们可以任何有依赖关系的两个点建一个子图,通过对这幅做一个割表示选择一种染色方法,而把另外三种的贡献割掉。
如此题我们现在对x同学和y同学建一个子图,设x同学选择文科的愉悦指数是
a
x
a_x
ax,选择理科的愉悦指数是
b
x
b_x
bx,y同学选择文科的愉悦指数是
a
y
a_y
ay,选择理科的愉悦指数是
b
y
b_y
by,同选择文科的愉悦指数是aa,同选择理科的愉悦指数是bb,则对子图:
就可以通过构造这四组割
a
+
e
+
d
=
a
x
+
b
y
+
a
a
+
b
b
b
+
c
+
e
=
a
y
+
b
x
+
a
a
+
b
b
a
+
b
=
a
x
+
a
y
+
a
a
c
+
d
=
b
x
+
b
y
+
b
b
\begin{aligned} a+e+d&=a_x+b_y+aa+bb\\ b+c+e&=a_y+b_x+aa+bb\\ a+b&=a_x+a_y+aa\\ c+d&=b_x+b_y+bb \end{aligned}
a+e+db+c+ea+bc+d=ax+by+aa+bb=ay+bx+aa+bb=ax+ay+aa=bx+by+bb
最后可以解方程得到
a
=
a
x
+
a
a
2
b
=
a
y
+
a
a
2
c
=
b
x
+
b
b
2
d
=
b
y
+
b
b
2
\begin{aligned}\\ a&=a_x+\frac{aa}{2}\\ b&=a_y+\frac{aa}{2}\\ c&=b_x+\frac{bb}{2}\\ d&=b_y+\frac{bb}{2} \end{aligned}
abcd=ax+2aa=ay+2aa=bx+2bb=by+2bb
而对于每个点其自身的权值只需要加一次就可以了,而和其他点之间的关联贡献则所有的边都必须加上。据此进行建图,跑出最小割,总的所有的贡献和减去最小割就是答案
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=10005;
const int maxm=2e5+5;
const int inf=0x3f3f3f3f;
struct Edge{
int to,nxt,cap,flow;
}edge[maxm];
int tol;
int head[maxn];
void init(){
tol=2;
memset(head,-1,sizeof(head));
}
void AddEdge(int u,int v,int w,int rw=0){
edge[tol].to=v;edge[tol].cap=w;edge[tol].flow=0;
edge[tol].nxt=head[u];head[u]=tol++;
edge[tol].to=u;edge[tol].cap=rw;edge[tol].flow=0;
edge[tol].nxt=head[v];head[v]=tol++;
}
int Q[maxn];
int dep[maxn],cur[maxn],sta[maxn];
bool bfs(int s,int t,int n){
int front=0,tail=0;
memset(dep,-1,sizeof(dep[0])*(n+1));
dep[s]=0;
Q[tail++]=s;
while(front<tail){
int u=Q[front++];
for(int i=head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if(edge[i].cap>edge[i].flow&&dep[v]==-1){
dep[v]=dep[u]+1;
if(v==t) return true;
Q[tail++]=v;
}
}
}
return false;
}
int dinic(int s,int t,int n){
int maxflow=0;
while(bfs(s,t,n)){
for(int i=0;i<n;i++) cur[i]=head[i];
int u=s,tail=0;
while(cur[s]!=-1){
if(u==t){
int tp=inf;
for(int i=tail-1;i>=0;i--)
{
tp=min(tp,edge[sta[i]].cap-edge[sta[i]].flow);
}
maxflow+=tp;
for(int i=tail-1;i>=0;i--){
edge[sta[i]].flow+=tp;
edge[sta[i]^1].flow-=tp;
if(edge[sta[i]].cap-edge[sta[i]].flow==0) tail=i;
}
u=edge[sta[tail]^1].to;
}
else if(cur[u]!=-1&&edge[cur[u]].cap>edge[cur[u]].flow&&dep[u]+1==dep[edge[cur[u]].to]){
sta[tail++]=cur[u];
u=edge[cur[u]].to;
}
else{
while(u!=s&&cur[u]==-1) u=edge[sta[--tail]^1].to;
cur[u] = edge [cur[u]].nxt;
}
}
}
return maxflow;
}
const int size=105;
int n,m;
int a[size][size],b[size][size];
int aay[size][size],bby[size][size];
int aax[size][size],bbx[size][size];
inline int Hash(int x,int y){return (x-1)*m+y;}
int main()
{
//freopen("p1646.in","r",stdin);
scanf("%d%d",&n,&m);
int ans=0;
init();
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
a[i][j]*=2;
ans+=a[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&b[i][j]);
b[i][j]*=2;
ans+=b[i][j];
}
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&aay[i][j]);
aay[i][j]*=2;
ans+=aay[i][j];
}
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&bby[i][j]);
bby[i][j]*=2;
ans+=bby[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<m;j++)
{
scanf("%d",&aax[i][j]);
aax[i][j]*=2;
ans+=aax[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<m;j++)
{
scanf("%d",&bbx[i][j]);
bbx[i][j]*=2;
ans+=bbx[i][j];
}
}
int s=0,t=n*m+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int aa=0,bb=0;
if(i-1>=1) aa+=aay[i-1][j],bb+=bby[i-1][j],AddEdge(Hash(i,j),Hash(i-1,j),(aay[i-1][j]+bby[i-1][j])/2);
if(i<n) aa+=aay[i][j],bb+=bby[i][j],AddEdge(Hash(i,j),Hash(i+1,j),(aay[i][j]+bby[i][j])/2);
if(j-1>=1) aa+=aax[i][j-1],bb+=bbx[i][j-1],AddEdge(Hash(i,j),Hash(i,j-1),(aax[i][j-1]+bbx[i][j-1])/2);
if(j<m) aa+=aax[i][j],bb+=bbx[i][j],AddEdge(Hash(i,j),Hash(i,j+1),(aax[i][j]+bbx[i][j])/2);
aa/=2;bb/=2;
aa+=a[i][j],bb+=b[i][j];
AddEdge(s,Hash(i,j),aa);
AddEdge(Hash(i,j),t,bb);
}
}
printf("%d\n",(ans-dinic(s,t,t+1))/2);
}