首页 > 代码库 > BZOJ 1565 NOI2009 植物大战僵尸 最大权闭合图+拓扑排序

BZOJ 1565 NOI2009 植物大战僵尸 最大权闭合图+拓扑排序

题目大意:给定一个m*n的草坪,每块草坪上的植物有两个属性:

1.啃掉这个植物,获得收益x(可正可负)

2.保护(r,c)点的植物不被啃掉

任何一个点的植物存活时,它左侧的所有植物都无法被攻击

求最大收益

首先这个保护和被保护的关系就是最大权闭合图的连边关系 然后直接跑就行

然后我们就会发现没过样例0.0

原因当图出现环时,根据题意,环上的所有点都不能取(想象一个无冷却的食人花前面放一个坚果)

所以这题还要去掉环

由于环上的点不能取,所以所有指向环上的点的点都不能取

这个条件看起来不太好做,我们反向处理

由于环上的点不能取,所以所有反向图中环上的点指向的点都不能取

于是拓扑排序就可以处理了 所有拓扑排序排不到的点全都删掉即可

比较囧的是我一开始写的Tarjan(这题不能写Tarjan),而且Tarjan还写挂了,两个错误一异或就AC了0.0 逗尿了

顺便吐槽一下测试数据只有两个点有环 欺骗我们感情0.0

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 610
#define INF 0x7fffffff
using namespace std;
struct abcd{
	int to,f,next;
}table[500500];
int head[M],tot=1;
int m,n,s,t,ans;
int q[65540],d[M],f[M],into[M];
unsigned short r,h;
bool usable[M];
void add(int x,int y,int z)
{
	table[++tot].to=y;
	table[tot].f=z;
	table[tot].next=head[x];
	head[x]=tot;
	table[++tot].to=x;
	table[tot].f=0;
	table[tot].next=head[y];
	head[y]=tot;
	into[x]++;
}
void Topology_Sort()
{
	int i;
	for(i=s;i<=t;i++)
		if(!into[i])
			q[++r]=i;
	while(r!=h)
	{
		int x=q[++h];
		usable[x]=1;
		if(f[x]>0)
			ans+=f[x];
		for(i=head[x];i;i=table[i].next)
			if(i&1)
			{
				into[table[i].to]--;
				if(!into[table[i].to])
					q[++r]=table[i].to;
			}
	}
}
bool BFS()
{
	int i;
	memset(d,0,sizeof d);
	r=h;q[++r]=s;d[s]=1;
	while(r!=h)
	{
		int x=q[++h];
		for(i=head[x];i;i=table[i].next)
			if( table[i].f && !d[table[i].to] && usable[table[i].to] )
			{
				d[table[i].to]=d[x]+1;
				q[++r]=table[i].to;
				if(table[i].to==t)
					return true;
			}
	}
	return false;
}
int Dinic(int x,int flow)
{
	int i,temp=flow;
	if(x==t)
		return flow;
	for(i=head[x];i;i=table[i].next)
		if( table[i].f && d[table[i].to]==d[x]+1 && temp )
		{
			int k=Dinic( table[i].to , min(temp,table[i].f) );
			if(!k)
				d[table[i].to]=0;
			temp-=k;
			table[i].f-=k;
			table[i^1].f+=k;
		}
	return flow-temp;
}

int main()
{
	
	//freopen("pvz.in","r",stdin);
	//freopen("pvz.out","w",stdout);
	
	int i,j,x,y,r,c;
	cin>>m>>n;
	s=0;t=m*n+1;
	for(i=1;i<=m*n;i++)
	{
		scanf("%d",&f[i]);
		if(f[i]>0)
			add(s,i,f[i]);
		else
			add(i,t,-f[i]);
		scanf("%d",&y);
		for(j=1;j<=y;j++)
		{
			scanf("%d%d",&r,&c);
			add(r*n+c+1,i,INF);
		}
		if(i%n)
			add(i,i+1,INF);
	}
	Topology_Sort();
	while( BFS() )
		ans-=Dinic(s,INF);
	cout<<ans<<endl;
}


BZOJ 1565 NOI2009 植物大战僵尸 最大权闭合图+拓扑排序