BZOJ1324Exca王者之剑&BZOJ1475方格取数——二分图最大独立集

时间:2023-12-15 16:46:14

题目描述

BZOJ1324Exca王者之剑&BZOJ1475方格取数——二分图最大独立集 BZOJ1324Exca王者之剑&BZOJ1475方格取数——二分图最大独立集

输入

第一行给出数字N,M代表行列数.N,M均小于等于100 下面N行M列用于描述数字矩阵

输出

输出最多可以拿到多少块宝石

样例输入

2 2
1 2
2 1

样例输出

4
题意就是选取一些点使他们互不相邻且使选取点的点权和最大。我们将网格图黑白染色,将相邻点连边,显然这是个二分图,我们要求的就是二分图的最大独立集。建模时将源点连向黑点,流量为点权;黑点连向与它相邻的白点,流量为$INF$;将白点连向汇点,流量为点权。答案就是总点权和$-$最小割,被割的边所连点就是不选取的点。因为最小割使源汇点不连通,所以所有选取的黑点都不会流向白点,所有选取的白点不会有黑点流过来,即相邻的点不会被同时选取。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
int head[12000];
int next[50000];
int to[50000];
int val[50000];
int d[12000];
int q[12000];
int n,m;
int tot=1;
int ans=0;
int S,T;
int s[200][200];
void add(int x,int y,int v)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=v;
tot++;
next[tot]=head[y];
head[y]=tot;
to[tot]=x;
val[tot]=0;
}
bool bfs(int S,int T)
{
int r=0;
int l=0;
memset(q,0,sizeof(q));
memset(d,-1,sizeof(d));
q[r++]=S;
d[S]=0;
while(l<r)
{
int now=q[l];
for(int i=head[now];i;i=next[i])
{
if(d[to[i]]==-1&&val[i]!=0)
{
d[to[i]]=d[now]+1;
q[r++]=to[i];
}
}
l++;
}
return d[T]!=-1;
}
int dfs(int x,int flow)
{
if(x==T)
{
return flow;
}
int now_flow;
int used=0;
for(int i=head[x];i;i=next[i])
{
if(d[to[i]]==d[x]+1&&val[i]!=0)
{
now_flow=dfs(to[i],min(flow-used,val[i]));
val[i]-=now_flow;
val[i^1]+=now_flow;
used+=now_flow;
if(now_flow==flow)
{
return flow;
}
}
}
if(used==0)
{
d[x]=-1;
}
return used;
}
void dinic()
{
while(bfs(S,T)==true)
{
ans-=dfs(S,0x3f3f3f);
}
}
int main()
{
scanf("%d%d",&m,&n);
S=n*m+1;
T=n*m+2;
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&s[i][j]);
ans+=s[i][j];
if((i+j)%2==0)
{
add(S,n*(i-1)+j,s[i][j]);
}
else
{
add(n*(i-1)+j,T,s[i][j]);
}
}
}
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
if((i+j)%2==0)
{
if(i-1>0)
{
add(n*(i-1)+j,n*(i-2)+j,1<<30);
}
if(j-1>0)
{
add(n*(i-1)+j,n*(i-1)+j-1,1<<30);
}
if(i+1<=m)
{
add(n*(i-1)+j,n*i+j,1<<30);
}
if(j+1<=n)
{
add(n*(i-1)+j,n*(i-1)+j+1,1<<30);
}
}
}
}
dinic();
printf("%d",ans);
}