首页 > 其他 > 详细

[NOI2009] 植物大战僵尸

时间:2020-02-20 21:49:43      阅读:79      评论:0      收藏:0      [点我收藏+]

码风略毒瘤

我怕不是疯了

正文

这题花了我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=='\r'||ch=='\n'||ch==' ') ch=get();
    while(ch!='\r'&&ch=='\n'&&ch!=' ')x+=ch,ch=get();
    return x;
}
char read_ch(){
    char ch=get();
    while(ch=='\r'||ch=='\n'||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聊天。。

技术分享图片

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

[NOI2009] 植物大战僵尸

原文:https://www.cnblogs.com/zhltao/p/12337345.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!