P4142 洞穴遇险(类三分图分类费用流)

读完题发现拐角处一定放在 i + j i+j 为奇数的地方

所以可以想办法转化为 3 L 3个格子凑成L形状权值的最大值 (因为要减去最多嘛)

然后就有一些性质了

当确定某个格子 ( i , j ) (i,j) 作为拐角放置

为了构成 L L 形状,还需要覆盖 2 2 个格子

可以是上左,上右,下左,下右

4 i + j 发现这4个格子的i+j都是偶数

i + j 那我们可以根据二分图对i+j的奇偶性染色

但这样不够

L , L 构成L形,相当于有一条路径从L的一端出发经过拐点到达另一个格子

价值是拐点的点权

, L i + j , 现在问题来了,L的两段i+j都是偶数,但他们又需要满足能彼此匹配

, L i + j , j , 可以发现,L两段虽然i+j都是偶数,但所处的j一个是奇数,一个是偶数

所以至此就把点分成了 3 3

L L 的构成可以看作从奇数列的偶数格子出发经过拐点到达偶数列的偶数格子

这样就可以构成一个三分图(也可以说四分图,因为拐点要被拆点了)

i + j i i , i i 1 , i+j为奇数的拐点拆为i和i',i向i'连一条流量1,费用为点权的边

, , 然后对于偶数格子,只能向部分相邻格子连边,比如

s , i 源点s连向奇数列的偶数格子,奇数列的偶数格子连向拐点的i

i , t i'连向偶数列的偶数格子,偶数列的偶数格子连向汇点t

(无特别说明,流量都是1,费用都是0)

#include <bits/stdc++.h>
using namespace std;
const int maxn=50009;
const int inf=1e9;
#define id(x,y) (x-1)*n+y
int a[509][509];
int n,m,s,t,k;
int maxflow,mincost;
int dis[maxn],head[maxn<<1],cnt=1,flow[maxn],pre[maxn],vis[maxn];
struct edge{
	int to,nxt,w,flow;//分别代表 
}d[maxn<<1];
void add(int u,int v,int flow,int w)//最大流量,单位费用
{
	d[++cnt]=(edge){v,head[u],w,flow},head[u]=cnt;
	d[++cnt]=(edge){u,head[v],-w,0},head[v]=cnt;
} 
bool spfa()
{
	queue<int>q;
	for(int i=0;i<=t;i++)	dis[i]=inf;
	memset(vis,0,sizeof(vis));
	q.push(s);
	dis[s]=0,vis[s]=1;
	flow[s] = inf;//初始流量无限大
	while( !q.empty() )
	{
		int u=q.front(); q.pop();
		vis[u]=0;//出队
		for(int i=head[u];i;i=d[i].nxt)
		{
			if( !d[i].flow )	continue;//无流量了	
			int v=d[i].to;
			if( dis[v]>dis[u]+d[i].w )
			{
				dis[v]=dis[u]+d[i].w;
				flow[v] = min(flow[u],d[i].flow);//更新当前流量
				pre[v]=i;//记录从哪条边过来的
				if( !vis[v] )	vis[v]=1,q.push(v); 
			}
		}	
	} 
	if( dis[t]==inf )	return 0;
	return 1;
}
int x[5]={0,0,1,-1},y[5]={1,-1,0,0};
int main()
{
	cin >> n >> m >> k;
	int sumn=0;
	s=0,t=n*n*2+1;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
		cin >> a[i][j],sumn+=a[i][j];
	for(int i=1;i<=k;i++)
	{
		int l,r; cin >> l >> r;
		a[l][r]=-1;
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	{
		if( a[i][j]==-1 )	continue;
		if( (i+j)%2 )//奇数格子
			add( id(i,j),id(i,j)+n*n,1,-a[i][j] );
		else//偶数格子 
		{
			if( j%2==1 )//奇数列
			{
				add(s,id(i,j),1,0);
				for(int q=0;q<4;q++)
				{
					int nx=i+x[q],ny=j+y[q];
					if( nx<1||nx>n||ny<1||ny>n )	continue;
					if( a[nx][ny]==-1 )	continue;
					add(id(i,j),id(nx,ny),1,0);
				}
			}
			else
			{
				add(id(i,j),t,1,0);	
				for(int q=0;q<4;q++)
				{
					int nx=i+x[q],ny=j+y[q];
					if( nx<1||nx>n||ny<1||ny>n )	continue;
					if( a[nx][ny]==-1 )	continue;
					add(id(nx,ny)+n*n,id(i,j),1,0);
				}
			} 
		} 
	}
	while( spfa()&&m )
	{
		if( dis[t]>0 )	break;
		int i,x=t;
		while( x!=s )
		{
			i=pre[x];
			d[i].flow-=flow[t],d[i^1].flow+=flow[t];
			x=d[i^1].to;
		}
		m-=flow[t];
		mincost+=dis[t]*flow[t];
	}
	cout << sumn+mincost;
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/108523598