• [NOI2009] 植物大战僵尸


    码风略毒瘤

    我怕不是疯了

    正文

    这题花了我3h。其实想法有了就好想,可惜,我没有想法。

    其实,一开始,做这个题我想了贪心和dp的方法,但是,因为一个很神奇的植物的保护范围,所以我把最后的尝试,放在了图论上

    开始时,其实我也不知道怎么建边,

    because 僵尸只能一步步的进攻

    • 建一个边,从每一行的前一个向后一个建边,表示一种 protect 关系

    then 植物有攻击范围

    • 建一个边,从保护人向受保护人建边,表示一种 protect 关系

    注意,这里的 a -> b 表示 a 保护 b

    然后,我想到了是否能用 tarjan 或者 topo 来求个环,然后这些植物就是无敌的,就不考虑了。

    当然,topo 的时候是考虑入度就行了,然而在我想完 topo 之后,我束手无策了。咋办,凉拌,不可做,走人(选择 topo 的原因是码量小,而且会比 topo 快,当然事实上 topo 还有更重要的一些优点)

    然后我的想法是,类似于接着做 topo,但是,发现没有最优子结构之后果断放弃。再想想是否可以树形dp(话说这是一个名词),就像 CF467D 它那题也是跑了一个 tarjan ,之后发现状态无法很好的定义,所以。。凉凉。

    我几乎放弃了图论的算法,准备写一个暴力,开阔思路。

    暴力思想如下:

    • 枚举放僵尸的过程
    • 终止条件是没有路能放僵尸
    • 注意:这是个回溯算法!
    • 时间复杂度是 (O(2^{nm^{m}})),哈哈

    崩了,崩了!

    丝毫没有起到开阔思路的操作,还增添了我对暴力的好感。。我又回归了图论

    ps:注意我现在操作的是反向图即 (forall (u,v) in E) 则有 vu 的保护

    我们知道,如果选了一个点,那么我一定要选出它的后继,所以,我陷入了 topo 的遐想,在之后崩了。

    我点开算法标签 最大流,???黑人问号,最大闭合子图,听都没听说过。于是我 baidu 了下然后,看了一下最大闭合子图的一些文章,真是豁然开朗!

    这个算法刚好能够解决我的问题,我们只需要建一个反图,然后这就满足了一个 $forall u in V' $ 且有 ((u,v) in E) 则必有 (v in E)的性质(这就是为啥叫闭合了)

    献上代码

    /* make by ltao */
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <algorithm>
    #include <queue>
    #include <deque>
    #include <stack>
    #include <vector>
    #include <set>
    #include <list>
    #include <map>
    #include <time.h>
    #include <fstream>
    typedef unsigned long long ull;
    typedef double db;
    typedef long long ll;
    #define fake int
    #define sz 666666
    #define inf sz
    #define get() getchar()
    using namespace std;
    string read_str(){
    	string x;
    	char ch=get();
    	while(ch=='
    '||ch=='
    '||ch==' ') ch=get();
    	while(ch!='
    '&&ch=='
    '&&ch!=' ')x+=ch,ch=get();
    	return x;
    }
    char read_ch(){
    	char ch=get();
    	while(ch=='
    '||ch=='
    '||ch==' ') ch=get();
    	return ch;
    }
    int read(){
    	int x=0;bool f=0;
    	char ch=get();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-') f=1;
    		ch=get();
    	}
    	while(ch<='9'&&ch>='0'){
    		x=(x<<1)+(x<<3)+(ch-'0');
    		ch=get(); 
    	}
    	return f?-x:x;
    }
    const int Maxm=2*1e5+11,Maxn=2002;
    int n,m,s,t,h[Maxn],cnt,maxflow,sco[Maxn],w,x,y,in[Maxn],sum,dep[Maxn],cur[Maxn];
    bool can[Maxn];
    struct Edge{
    	int fr,to,lac,flow;
    	void insert(int x,int y,int z){
    		fr=x;to=y;
    		lac=h[x];
    		h[x]=cnt++;
    		flow=z;
    	}
    }edge[Maxm],vedge[Maxm];
    
    bool bfs(int s,int t){
    	memset(dep,-1,sizeof dep);
    	memcpy(cur,h,sizeof cur);
    	queue<int> q;
    	q.push(s);dep[s]=0;
    	while(!q.empty()){
    		int fr=q.front();
    		q.pop();
    		for(int i=h[fr];i!=-1;i=edge[i].lac){
    			int to=edge[i].to;
    			if(!edge[i].flow||dep[to]!=-1) continue;
    			dep[to]=dep[fr]+1;
    			q.push(to);
    			if(to==t){
    				while(!q.empty()) q.pop();
    				return 1;
    			}
    		} 
    	}
    	return 0;
    }
    int dfs(int u,int min1){
    	if(u==t) return min1;
    	int sum=min1;
    	for(int i=cur[u];i!=-1;i=edge[i].lac){
    		int to=edge[i].to;
    		if(!edge[i].flow||dep[to]!=dep[u]+1) continue;
    		int ret=dfs(to,min(sum,edge[i].flow));
    		cur[u]=i;
    		edge[i].flow-=ret,edge[i^1].flow+=ret,sum-=ret;
    		if(sum==0) break;
    	}
    	return min1-sum;
    }
    void Dinic(int s,int t){
    	while(bfs(s,t)) 
    		maxflow+=dfs(s,0x3f3f3f3f);
    }
    int main() {
    	n=read();m=read();s=0;t=n*m+1;
    	memset(h,-1,sizeof h);
    	for(int i=1;i<=n*m;i++){
    		sco[i]=read();
    		w=read();
    		while(w--){
    			x=read();y=read();
    			edge[cnt].insert(i,x*m+y+1,0);
    			in[x*m+y+1]++;
    		}
    		//做他保护的 
    		if(i%m!=1){
    			edge[cnt].insert(i,i-1,0);
    			in[i-1]++;
    		}
    		//连接之前的 
    	}
    	//topo
    	queue <int> q;
    	for(int i=m;i<=n*m;i+=m)
    		if(in[i]==0){
    			q.push(i);
    		}
    	while(!q.empty()){
    		int fr=q.front();q.pop();
    		can[fr]=1;
    		for(int i=h[fr];i!=-1;i=edge[i].lac){
    			int to=edge[i].to;
    			in[to]--;
    			if(!in[to]) q.push(to);
    		}
    	}
    	//做topo
    	int vcnt=cnt;cnt=0;
    	memset(h,-1,sizeof h);
    	memcpy(vedge,edge,sizeof vedge); 
    	for(int i=0;i<vcnt;i++){
    		int to=vedge[i].to,fr=vedge[i].fr;
    		if(can[fr]&&can[to]){
    			//由于最大权闭合子图 
    			//我们 to 收到 fr 的保护
    			//所以有 to 必有 fr
    			edge[cnt].insert(to,fr,inf);
    			edge[cnt].insert(fr,to,0);
    		}
    	}
    	//连接汇点和源点 
    	for(int i=1;i<=n*m;i++)
    		if(can[i]){
    			if(sco[i]>0){
    				edge[cnt].insert(s,i,sco[i]);
    				edge[cnt].insert(i,s,0);
    				sum+=sco[i];
    			}
    			if(sco[i]<0){
    				edge[cnt].insert(i,t,-sco[i]);
    				edge[cnt].insert(t,i,0);
    			}
    		}
    	Dinic(s,t);
    	printf("%d",sum-maxflow);
    	return 0;
    }
    

    主要的思想是 topo+Dinic 就好了

    其实,自己意会一下就可以知道那个算法的正确性

    是的,我就是疯了

    我怎么会跟qs聊天。。

    跟她一起,我真是越来越不正常了

  • 相关阅读:
    构建者模式第二种方式
    设计模式装饰设计模式
    设计模式观察者模式
    设计模式模板方法模式
    分页工具类
    构建者模式第一种方式
    设计模式策略模式
    防止反序列创建对象漏洞
    单例设计模式枚举单例
    python 查找多边形上最近点的坐标
  • 原文地址:https://www.cnblogs.com/zhltao/p/12337345.html
Copyright © 2020-2023  润新知