• 【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;
    }


  • 相关阅读:
    团队作业 总结
    个人作业 Alpha项目测试
    第二次作业
    交互式多媒体图书平台的设计与实现
    基于VS Code的C++语言的构建调试环境搭建指南
    码农的自我修养之必备技能 学习笔记
    工程化编程实战callback接口学习
    如何测评一个软件工程师的计算机网络知识水平和编程能力
    深入理解TCP协议及其源代码
    Socket与系统调用深度分析
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793822.html
Copyright © 2020-2023  润新知