bzoj-1565 植物大战僵尸

141 篇文章 0 订阅
44 篇文章 0 订阅

题意:

给出一片n*m的草坪,上面每个点有一个植物;

现在由你来从最右面放出一些僵尸来进攻这些植物;

僵尸到一个植物面前的时候就可以吃掉这个植物,并且得到这个植物的的得分(可正可负);

每个植物可以攻击几个位置,并且僵尸只能从右面出发,也就是说在吃掉右面的植物之前不可能吃掉左面的;

僵尸是无限的,求最大得分;

n<=20,m<=30;


题解:

原来这个就是最大权闭合子图啊。。似乎以前没刷过的样子= =

总之先建图:

将每个植物向能保护它的植物连流量为无穷的边;

从源点向正得分植物连流量为得分的边;

从负得分植物向汇点连流量为得分相反数的边;

最后用所有正权值的和减去最小割就是了;

这样建图解决最大权闭合子图问题是正确的,感性上讨论一下各种边就差不多理解的了吧;

然而通常的最大权闭合子图都是拓扑图,在这个问题中可以有环存在;

所以有些点是永远也不可能取到的,这些点我们不要,也就是拓扑排序之后排不到的点;

然后建图就是正确的了,跑最大权闭合子图即可;


代码:


#include<queue>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 40
#define E 400000
using namespace std;
int next[E],to[E],flow[E],head[N*N],ce=1;
int dis[N*N],S,T,sum;
int val[N*N];
queue<int>q;
void add(int x,int y,int fl)
{
	to[++ce]=y;
	flow[ce]=fl;
	next[ce]=head[x];
	head[x]=ce;
	to[++ce]=x;
	flow[ce]=0;
	next[ce]=head[y];
	head[y]=ce;
}
bool BFS()
{
	int x,i;
	memset(dis,0,sizeof(dis));
	dis[S]=1;
	q.push(S);
	while(!q.empty())
	{
		x=q.front(),q.pop();
		for(i=head[x];i;i=next[i])
		{
			if(flow[i]&&!dis[to[i]])
			{
				dis[to[i]]=dis[x]+1;
				q.push(to[i]);
			}
		}
	}
	return dis[T]!=0;
}
int dfs(int x,int lim)
{
	if(x==T)	return lim;
	int ret=0,i,temp;
	for(i=head[x];i;i=next[i])
	{
		if(flow[i]&&dis[to[i]]==dis[x]+1)
		{
			temp=dfs(to[i],min(flow[i],lim-ret));
			ret+=temp;
			flow[i]-=temp,flow[i^1]+=temp;
			if(ret==lim)
				return ret;
		}
	}
	if(!ret)	dis[x]=0;
	return ret;
}
namespace Graph
{
	int next[E],to[E],head[N*N],ce;
	int in[N*N];
	bool vis[N*N];
	void add(int x,int y)
	{
		to[++ce]=y;
		next[ce]=head[x];
		head[x]=ce;
		in[y]++;
	}
	void slove(int n,int m)
	{
		int i,x,tot=n*m;
		for(x=0;x<tot;x++)
		{
			if(!in[x])
				q.push(x);
		}
		while(!q.empty())
		{
			x=q.front(),q.pop();
			vis[x]=1;
			for(i=head[x];i;i=next[i])
			{
				in[to[i]]--;
				if(!in[to[i]])
					q.push(to[i]);
			}
		}
		for(x=0;x<tot;x++)
		{
			if(!vis[x])	continue;
			if(val[x]>=0)
				::add(S,x,val[x]),sum+=val[x];
			else
				::add(x,T,-val[x]);
			for(i=head[x];i;i=next[i])
			{
				if(vis[to[i]])
					::add(to[i],x,0x3f3f3f3f);
			}
		}
	}
}
int main()
{
	int n,m,w,i,j,k,x,y,ans;
	scanf("%d%d",&n,&m);
	S=n*m,T=S+1;
	for(i=0,sum=0;i<n;i++)
	{
		for(j=0;j<m;j++)
		{
			if(j)	Graph::add(i*m+j,i*m+j-1);
			scanf("%d%d",&val[i*m+j],&w);
			for(k=1;k<=w;k++)
			{
				scanf("%d%d",&x,&y);
				Graph::add(i*m+j,x*m+y);
			}
		}
	}
	Graph::slove(n,m);
	ans=0;
	while(BFS())
		ans+=dfs(S,0x3f3f3f3f);
	printf("%d\n",sum-ans);
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值