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;
    }
    
    
  • 相关阅读:
    jquery实现选项卡(两句即可实现)
    常用特效积累
    jquery学习笔记
    idong常用js总结
    织梦添加幻灯片的方法
    LeetCode "Copy List with Random Pointer"
    LeetCode "Remove Nth Node From End of List"
    LeetCode "Sqrt(x)"
    LeetCode "Construct Binary Tree from Inorder and Postorder Traversal"
    LeetCode "Construct Binary Tree from Preorder and Inorder Traversal"
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10189176.html
Copyright © 2011-2022 走看看