zoukankan      html  css  js  c++  java
  • 种树 (堆模拟网络流)

    种树

    题目描述

    cyrcyr今天在种树,他在一条直线上挖了n个坑。这n个坑都可以种树,但为了保证每一棵树都有充足的养料,cyrcyr不会在相邻的两个坑中种树。而且由于cyrcyr的树种不够,他至多会种k棵树。假设cyrcyr有某种神能力,能预知自己在某个坑种树的获利会是多少(可能为负),请你帮助他计算出他的最大获利。

    输入输出格式

    输入格式:

    第一行,两个正整数n,k。

    第二行,n个正整数,第i个数表示在直线上从左往右数第i个坑种树的获利。

    输出格式:

    输出1个数,表示cyrcyr种树的最大获利。

    输入输出样例

    输入样例#1: 复制

    6 3
    100 1 -1 100 1 -1

    输出样例#1: 复制

    200

    说明

    对于20%的数据,n<=20。

    对于50%的数据,n<=6000。

    对于100%的数据,n<=500000,k<=n/2,在一个地方种树获利的绝对值在1000000以内。

    题解

    一道非常好的堆模拟网络流题。
    首先我们来想想(O(n^2))
    很显然的两重循环dp
    第一重枚举第i个位置,第二重枚举选了j颗树。
    对于O(n)的模拟。
    我们首先明确选了 i 就不能选 i-1 和 i+1。
    所以所有的情况都是由选 i 和选 i-1 ,i+1 产生的。
    选 i 时, i 的贡献应当是大于 i-1 和 i+1 的。
    但是当我们发现选 i-1 和 i+1 要更好时呢?
    网络流有一个操作就是反悔。
    我们是不是也可以反悔一下呢?
    因为先选了 i ,那么下一次选择的时候,如果反悔就是选两边
    就是选了周围两个点,不反悔就是选其他点
    那么把周围两个点维护为一个点就可以了。
    (vi[i]=vi[l[i]]+vi[r[i]]-vi[i])
    然后维护一下 (l)(r) 数组。

    代码

    #include<cstdio>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    using namespace std;
    priority_queue<pair<int,int> >q;
    int vis[1000001],ch[1000001],n,m;
    long long ans;
    struct node{
        int vi,id,l,r;
    }t[1000001];
    int read(){
        int x=0,w=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*w;
    }
    
    int main(){
        n=read();m=read();
        for(int i=1;i<=n;i++){
            ch[i]=t[i].vi=read();t[i].id=i;
            t[i].l=i-1;t[i].r=i+1;
            q.push(make_pair(ch[i],i));
        }
        t[0].r=1;t[n+1].l=n;
        while(m&&!q.empty()){
            while(vis[q.top().second])q.pop();
            int x=q.top().second;q.pop();
            if(t[x].vi<0)break;
            ans+=t[x].vi;
            t[x].vi=t[t[x].l].vi+t[t[x].r].vi-t[x].vi;
            vis[t[x].l]=vis[t[x].r]=1;
            t[x].l=t[t[x].l].l;t[t[x].l].r=x;
            t[x].r=t[t[x].r].r;t[t[x].r].l=x;
            q.push(make_pair(t[x].vi,x));
            m--;
        }
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    素数路径Prime Path POJ3126 素数,BFS
    Fliptile POJ3279 DFS
    Find the Multiple POJ1426
    洗牌Shuffle'm Up POJ3087 模拟
    棋盘问题 POJ1321 DFS
    抓住那只牛!Catch That Cow POJ3278 BFS
    Dungeon Master POJ2251 三维BFS
    Splitting into digits CodeForce#1104A
    Ubuntu下手动安装Nvidia显卡驱动
    最大连续子序列和
  • 原文地址:https://www.cnblogs.com/hhh1109/p/9571549.html
Copyright © 2011-2022 走看看