zoukankan      html  css  js  c++  java
  • 洛谷2805 [NOI2009]植物大战僵尸 (拓扑排序+最小割)

    坚决抵制长题面的题目!

    首先观察到这个题目中,我们会发现,我们对于原图中的保护关系(一个点右边的点对于这个点也算是保护)
    相当于一种依赖。

    那么不难看出这个题实际上是一个最大权闭合子图模型。

    我们直接对于权值为负数的边,(S ightarrow now),流量是(-a[i][j]),表示打掉他要花这么多的代价。
    对于权值为正的边,(now ightarrow T) ,流量是(a[i][j]),表示如果割掉这个边,表示放弃他的收益。

    (这里之所以(S和T)不能反过来,因为我们跑最小割的时候,是要保证S到T不连通,所以你要让负的权值与S相连,才会让依赖关系有意义)

    对于每个点,向他保护的点连边,边权为(inf)。表示这个关系不能打破。
    同时一个点右边的点,向这个点连边,流量也是(inf),因为右边的点要比左边的点先被打。

    最后的答案就是(正权值的sum - 最小割)(总的收益,减去花费和舍去的。)

    那么建出来图,我们会发现其实这个是有问题的。

    因为可能存在环的情况。
    (A保护B,B保护A)

    那么应该怎么办呢?
    我们发现如果存在一个环,那么环能保护到的点,以及再往后的点,都是无敌的!

    所以合法的点,就是从起点开始,找到所有的满足起点到这个点的所有路径都不经过环的 点。

    那么这个可以通过拓扑排序来实现。
    qwq
    只需要一开始先把所有的依赖关系,跑一遍拓扑排序。
    然后选出来有效的点,再建图就ok

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define mk make_pair
    #define ll long long
    #define pb push_back
    using namespace std;
    inline int read()
    {
      int x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    const int maxn = 3010;
    const int maxm = 2e6+1e2;
    const int inf = 1e9;
    int n,m,cnt=1;
    int point[maxn],nxt[maxm],to[maxm],val[maxm];
    int h[maxn],in[maxn];
    int s,t;
    int a[maxn][maxn];
    int sum;
    int num;
    int dfn[maxn];
    int tag[maxn];
    vector<int> v[maxn];
    void init()
    {
    	cnt=1;
    	memset(point,0,sizeof(point));
    	memset(in,0,sizeof(in));
    }
    void add(int x,int y)
    {
    	nxt[++cnt]=point[x];
    	to[cnt]=y;
    	point[x]=cnt;
    	in[y]++;
    }
    void addedge(int x,int y,int w)
    {
    	nxt[++cnt]=point[x];
    	to[cnt]=y;
    	val[cnt]=w;
    	point[x]=cnt;
    }
    void insert(int x,int y,int w)
    {
    	addedge(x,y,w);
    	addedge(y,x,0);
    }
    queue<int> q;
    bool bfs(int s)
    {
    	memset(h,-1,sizeof(h));
    	h[s]=0;
    	q.push(s);
    	while (!q.empty())
    	{
    		int x = q.front();
    		q.pop();
    		for (int i=point[x];i;i=nxt[i])
    		{
    			int p=to[i];
    			if (h[p]==-1 && val[i]>0)
    			{
    				h[p]=h[x]+1;
    				q.push(p);
    			}
    		}
    	}
    	if (h[t]==-1) return false;
    	return true;
    }
    int dfs(int x,int low)
    {
        if (x==t || low==0) return low;
        int totflow=0;
        for (int i=point[x];i;i=nxt[i])
        {
        	int p = to[i];
        	if (h[p]==h[x]+1 && val[i]>0)
        	{
        		int tmp = dfs(p,min(low,val[i]));
        		val[i]-=tmp;
        		val[i^1]+=tmp;
        		low-=tmp;
        		totflow+=tmp;
        		if (low==0) return totflow;
    		}
    	}
    	if (low>0) h[x]=-1;
    	return totflow;
    }
    int dinic()
    {
    	int ans=0;
    	while (bfs(s))
    	{
    		ans=ans+dfs(s,inf);
    	}
    	return ans;
    } 
    int getnum(int x,int y)
    {
    	return (x-1)*m+y;
    }
    void tpsort()
    {
    	while (!q.empty()) q.pop();
    	for (int i=1;i<=num;i++)
    	{
    		if (!in[i]) q.push(i),tag[i]=1,dfn[i]=1;
    	}
    	while (!q.empty())
    	{
    		int x = q.front();
    		q.pop();
    		for (int i=point[x];i;i=nxt[i])
    		{
    			int p=to[i];
    			in[p]--;
    			if (!in[p])
    			{
    				dfn[p]=dfn[x]+1;
    				q.push(p);
    				tag[p]=1;
    			}
    		}
    	}
    }
    int main()
    {
      n=read(),m=read();
      num=n*m;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
        	a[i][j]=read();
            int num = read();
            for (int k=1;k<=num;k++)
            {
            	int x=read(),y=read();
            	x++,y++; 
            	v[getnum(i,j)].pb(getnum(x,y));
            	add(getnum(i,j),getnum(x,y));
    		}
    	    if (j!=m) add(getnum(i,j+1),getnum(i,j));
    	}
      tpsort();
      init();
      s=maxn-10;
      t=s+1;
      for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
        	int now=getnum(i,j);
        	if(!tag[now]) continue;
        	if(a[i][j]>0) sum+=a[i][j];
        	for (int k=0;k<v[now].size();k++)
        	  if (tag[v[now][k]]) insert(now,v[now][k],inf);
        	if (a[i][j]>0) insert(now,t,a[i][j]);
        	else insert(s,now,-a[i][j]);
    	}
      for (int i=1;i<=n;i++)
      {
      	 for (int j=1;j<=m;j++)
      	 {
      	 	int now = getnum(i,j);
      	 	int ri  = getnum(i,j+1);
      	 	if (j==m) continue;
      	 	if (!tag[now] || !tag[ri]) continue;
      	 	if (dfn[ri]>dfn[now]) swap(now,ri);
      	 	insert(ri,now,inf);
    	 }
      }
      cout<<sum-dinic();
      return 0;
    }
    
    
  • 相关阅读:
    python中的GIL
    centos7 安装docker
    ORACLE INSERT INTO SELECT
    Java substring几个用例
    Java Date类型转换、操作等(util.Date sql.Date,)
    ORACLE 按字段去除重复数据
    OFFICE技巧汇编
    ORACLE自动类型转换的坑
    ubuntu下,pycharm svn 版本控制,svn服务器在win下
    【草稿】pip重要命令;python 变量命名规则
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10189176.html
Copyright © 2011-2022 走看看