【NOI2009T4】植物大战僵尸-最大权闭合子图+拓补排序

测试地址:植物大战僵尸

做法:上次我们提到了最大权闭合子图的模型(见这里),这一个题目也容易看出是最大权闭合子图的模型,当一个植物被另一个植物保护的时候,隐含的关系就是:如果要选择攻击被保护植物,就必须也攻击保护植物,因此从被保护植物向保护植物连边,然后按照方法来建图求解。

然而这还不够,在纯粹的求解最大权闭合子图的过程中,环要么就不取,要么就全部取走,而这一题的条件限定如果存在环一定不能取,进而指向环的所有点也不能取,那么怎么把这些点剔除掉呢?答案就是拓补排序。对原图的反图进行拓补排序,最后没有入过队的点就是不能取的点了,然后再跑最大流即可。

以下是本人代码(注意!本人的代码会TLE,只能拿到90分,有待学习):

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define inf 1000000000
using namespace std;
int n,m,first[1010]={0},tot=1;
int level[1010],sum=0,val[1010],in[1010]={0};
bool vis[1010]={0};
struct edge {int v,f,next;} e[1000010];

void insert(int a,int b,int f)
{
  e[++tot].v=b,e[tot].f=f,e[tot].next=first[a],first[a]=tot;
  e[++tot].v=a,e[tot].f=0,e[tot].next=first[b],first[b]=tot;
}

void clear()
{
  queue<int> q;
  for(int i=1;i<=n*m;i++)
    if (!in[i]) q.push(i);
  while(!q.empty())
  {
    int v=q.front();q.pop();
	for(int i=first[v];i;i=e[i].next)
	  if (e[i].v!=n*m+1&&e[i].v!=0&&!e[i].f)
	  {
	    in[e[i].v]--;
		if (!in[e[i].v]) q.push(e[i].v);
      }
	vis[v]=1;
  }
  
  for(int i=1;i<=n*m;i++)
    if (vis[i]&&val[i]>0) sum+=val[i];
}

bool makelevel()
{
  queue<int> q;
  memset(level,0,sizeof(level));
  level[0]=1;
  q.push(0);
  
  while(!q.empty())
  {
    int v=q.front();q.pop();
	for(int i=first[v];i;i=e[i].next)
	  if (!level[e[i].v]&&e[i].f&&vis[e[i].v])
	  {
	    level[e[i].v]=level[v]+1;
		q.push(e[i].v);
	  }
  }
  
  return level[n*m+1];
}

int dfs(int v,int maxf)
{
  int ret=0,f;
  if (v==n*m+1) return maxf;
  for(int i=first[v];i;i=e[i].next)
    if (level[e[i].v]==level[v]+1&&e[i].f&&vis[e[i].v])
	{
	  f=dfs(e[i].v,min(maxf-ret,e[i].f));
	  e[i].f-=f;
	  e[i^1].f+=f;
	  ret+=f;
	  if (ret==maxf) return ret;
	}
  return ret;
}

int dinic()
{
  int ans=0;
  while(makelevel())
  {
    ans+=dfs(0,inf);
  }
  return ans;
}

int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1,b;i<=n*m;i++)
  {
    scanf("%d%d",&val[i],&b);
	while(b--)
	{
	  int x,y;
	  scanf("%d%d",&x,&y);
	  insert(x*m+y+1,i,inf);
	  in[x*m+y+1]++;
	}
  }
  for(int i=0;i<n;i++)
    for(int j=0;j<m-1;j++)
	{
	  insert(i*m+j+1,i*m+j+2,inf);
	  in[i*m+j+1]++;
    }
  clear();
  
  for(int i=1;i<=n*m;i++)
  {
    if (val[i]>0) insert(0,i,val[i]);
	if (val[i]<0) insert(i,n*m+1,-val[i]);
  }
  
  vis[n*m+1]=1;
  printf("%d",sum-dinic());
  
  return 0;
}


posted @ 2017-03-11 17:25  Maxwei_wzj  阅读(78)  评论(0编辑  收藏  举报