• 【BZOJ1565】 植物大战僵尸


     

    Description

    Input

    Output

      仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。

    Sample Input

      3 2
      10 0
      20 0
      -10 0
      -5 1 0 0
      100 1 2 1
      100 0

    Sample Output

      25
     

    Solution

      按照依赖关系建立有向图:若清除$a$前必须清除$b$,则连边$a$至$b$。这些依赖关系包括同行植物左边对右边的依赖,以及被保护者对保护者的依赖。

      首先不能考虑开挂集团,也就是依赖关系成强联通分量的植物,也包括能通过依赖关系走到强联通分量的植物——它们都无敌,需要排除掉。这可以用tarjan加上反向边的深搜预处理。

      

      一个植物能够下手,当且仅当其出度为0.

      每一株植物又是带正负权值的,那么明显是要在依赖图中,求一个最大权闭合子图。也就是要有植物起手(迎合闭合的性质,出边都在闭合子图内,即依赖的植物都要一起干掉,都应该在闭合子图的范围内),且干掉的植物权值和最大。

      按照最大权闭合子图的建立方法:源点向所有正权点连容量为其权值的边,所有负权点向汇点连容量为其权值绝对值的边;所有节点按依赖关系连边,容量为$+infty$。

      答案是所有正权点点权值和减去最大流。能将负权转化为正权来跑网络流的原因,拆拆括号就可以发现,这是很巧妙的。


    #include <cstdio>
    #include <queue>
    #include <vector>
    using namespace std;
    const int N=610,INF=2147000000;
    int n,m,a[N],h[N],hr[N],tot;
    int dfn[N],low[N],tmcnt,is[N],st[N],top,ins[N],vis[N];
    int S,T,dis[N],cur[N];
    int sum;
    queue<int> q;
    vector<int> list[N];
    struct Edge{int v,f,next;}g[1000010];
    inline int min(int x,int y){return x<y?x:y;}
    inline int id(int x,int y){return (x-1)*m+y;}
    inline void addEdge(int u,int v){
        g[++tot].v=v; g[tot].next=h[u]; h[u]=tot;
        g[++tot].v=u; g[tot].next=hr[v]; hr[v]=tot;
    }
    inline void addEdge(int u,int v,int f){
        g[++tot].v=v; g[tot].f=f; g[tot].next=h[u]; h[u]=tot;
        g[++tot].v=u; g[tot].f=0; g[tot].next=h[v]; h[v]=tot;
    }
    bool bfs(){
        while(!q.empty()) q.pop();
        q.push(S);
        for(int i=1;i<=T;i++) dis[i]=-1;
        dis[S]=0;
        while(!q.empty()){
            int u=q.front(); q.pop();
            for(int i=h[u],v;i;i=g[i].next)
                if(g[i].f&&dis[v=g[i].v]==-1){
                    dis[v]=dis[u]+1;
                    if(v==T) return true;
                    q.push(v);
                }
        }
        return dis[T]!=-1;
    }
    int dfs(int u,int delta){
        if(u==T) return delta;
        int ret=0,get;
        for(int i=cur[u],v;i&&delta;i=g[i].next)
            if(g[i].f&&dis[v=g[i].v]==dis[u]+1){
                get=dfs(v,min(delta,g[i].f));
                g[i].f-=get; g[i^1].f+=get;
                if(g[i].f) cur[u]=i;
                delta-=get; ret+=get;
            }
        if(!ret) dis[u]=-1;
        return ret;
    }
    int dinic(){
        int ret=0;
        while(bfs()){
            for(int i=1;i<=T;i++) cur[i]=h[i];
            ret+=dfs(S,INF);
        }
        return ret;
    }
    void tarjan(int u,int fa){
        dfn[u]=low[u]=++tmcnt;
        st[++top]=u; ins[u]=1;
        for(int i=h[u],v;i;i=g[i].next){
            v=g[i].v;
            if(!dfn[v]){
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
            }
            else if(ins[v])
                low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u]){
            if(st[top]==u){
                ins[u]=0;
                top--;
                return;
            }
            int x;
            do{
                x=st[top--];
                ins[x]=0;
                is[x]=1;
            }while(x!=u);
        }
    }
    void clear(int u){
        vis[u]=1; is[u]=1;
        for(int i=hr[u],v;i;i=g[i].next)
            if(!vis[v=g[i].v])
                clear(v);
    }
    int main(){
        freopen("input.in","r",stdin);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                int k=id(i,j),l,x,y;
                scanf("%d%d",&a[k],&l);
                while(l--){
                    scanf("%d%d",&x,&y);
                    addEdge(id(x+1,y+1),k);
                    list[id(x+1,y+1)].push_back(k);
                }
            }
        for(int i=1;i<=n;i++)
            for(int j=1;j<m;j++)
                addEdge(id(i,j),id(i,j+1));
        for(int i=1,up=n*m;i<=up;i++) 
            if(!dfn[i]) 
                tarjan(i,0);
        for(int i=1,up=n*m;i<=up;i++)
            if(!vis[i]&&is[i])
                clear(i);
        for(int i=1,up=n*m;i<=up;i++) h[i]=0;
        tot=1;
        S=n*m+1; T=n*m+2;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                int x=id(i,j);
                if(j<m&&!is[x]&&!is[id(i,j+1)])
                    addEdge(x,id(i,j+1),INF);
                if(is[x]) continue;
                if(a[x]>=0){
                    addEdge(S,x,a[x]);
                    sum+=a[x];
                }
                else addEdge(x,T,-a[x]);
                int sz=list[x].size();
                for(int k=0;k<sz;k++)
                    addEdge(x,list[x][k],INF);
            }
        int maxflow=dinic();    
        printf("%d
    ",sum-maxflow);
        return 0;
    }
    奇妙代码

      

  • 相关阅读:
    排序-冒泡排序
    模拟Excel同一列相同值的单元格合并
    MATLAB实例:绘制折线图
    MATLAB实例:非线性曲线拟合
    MATLAB实例:聚类初始化方法与数据归一化方法
    删除Word中出现的空白页
    MATLAB实例:为匹配真实标签,对训练得到的标签进行调整
    MATLAB实例:Munkres指派算法
    MATLAB实例:构造网络连接图(Network Connection)及计算图的代数连通度(Algebraic Connectivity)
    Successive Convex Approximation (SCA)
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/8016697.html
Copyright © 2020-2023  润新知