• [洛谷P1484] 种树


    题目类型:堆+贪心

    传送门:>Here<

    题意:有(N)个坑,每个坑可以种树,且获利(a[i])(可以为负)。任何相邻两个坑里不能都种树,问在最多种(K)棵树的前提下的最大获利

    解题思路

    第一眼觉得是(DP),但是数据太大(NK)显然不行……

    如果不约束相邻两个坑不能都种,那么显然是取最大的几个正数。只需排序求解即可

    回到问题,一步一步分析吧。

    假设(K=1),那么此时必然选最大的。

    假设(K=2),可以选择最大的(a[i]),并且对于所有(j eq i-1 且 j eq i+1),选择(a[j])。但是存在一个问题,加入存在这样的情况:(99 100 99)。因此还有可能的最优方案是不选择(a[i]),选取(a[i-1]和a[i+1])。并且我们会发现,出现这种情况时,必定同时选择(a[i-1]和a[i+1]),因为(a[i])才是最大的

    由于这种情况的存在,普通的做法似乎毫无头绪。联系网络流中我们引入了反向边的思想,意在反悔之前作出的决策。那么放在本题也一样。我们先去选择那个最大的,然后通过一种方式消除影响后选择两边的。于是,我们可以用一个大根堆维护所有的坑,选出最大的以后,删除最大值(a[i]),并推入(p=a[i-1]+a[i+1]-a[i])。如果(p>0),意味着(a[i-1]+a[i+1]>a[i])

    但是注意,并不是每次碰到这种情况我们都要去选,毕竟选择(a[i])只需要一颗树,而选择(a[i-1]+a[i+1])要两棵树。因此依然按照大根堆的规定来行使。容易发现,在堆里,我们不仅需要存值,还需要存位置

    下一步,如何实现?这是个比较困难的问题,在此之前还需要对问题进行深入分析

    前面我们得到结论,要么选择(a[i]),要么选择(a[i-1]+a[i+1])。这就好像(a[i-1] .. a[i+1])是一个节点一样,初始值是(a[i]),选过之后值成为了(a[i-1]+a[i+1]-a[i])。并且我们也会发现,选择两边的节点的话将会导致(a[i-2]和a[i+2])也不能选,正好像一个节点两侧的节点一样。

    联系缩点的思想,选择完一次以后,就可以将其左右缩点。此后这个点的权值就变了,但是依然是个正常的点。我们维护数组(L[i]和R[i])表示点(i)的左右侧(缩点之后)。初始时(L[i]=i-1, R[i]=i+1)。每一次缩点之后往左右两侧拓展即可。当然,切不可用(i-1)来代替左右侧,因为此时我们考虑的点全都得当成是缩完之后的点

    注意,缩点以后这个点内部的原先点的个数一定是奇数个。因此我们把信息全部存在中间那个点上,并且不允许访问那些其他被缩掉的点。用布尔数组记录,如果大根堆的堆顶访问到的是已经被标记过的则强行弹出。如果遇到负数的则意味着再选下去肯定不会优,直接结束。

    Code

    已压行

    /*By DennyQi 2018*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    #define  r  read()
    using namespace std;
    typedef long long ll;
    const int MAXN = 500010;
    const int INF = 1061109567;
    inline int Max(const int a, const int b){ return (a > b) ? a : b; }
    inline int Min(const int a, const int b){ return (a < b) ? a : b; }
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    struct Tree{ ll val; int idx; };
    inline bool operator < (const Tree& a, const Tree& b){ return a.val < b.val; }
    int N,K,x,used[MAXN],L[MAXN],R[MAXN]; ll ans,a[MAXN];
    priority_queue <Tree> q;
    int main(){
    	N = r, K = r;
    	for(int i = 1; i <= N; ++i){ q.push((Tree){a[i]=r, i}); L[i] = i-1, R[i] = i+1; }
    	while(K--){
    		while(used[q.top().idx]) q.pop();
    		if(q.top().val <= 0) break;
    		ans += 1LL * q.top().val, x = q.top().idx; q.pop();
    		a[x] = a[L[x]] + a[R[x]] - a[x]; 
    		used[L[x]] = used[R[x]] = 1;
    		L[x] = L[L[x]], R[L[x]] = x; R[x] = R[R[x]], L[R[x]] = x;
    		q.push((Tree){1LL * a[x], x});
    	}
    	printf("%lld", ans);
    	return 0;
    }
    
  • 相关阅读:
    PAT 解题报告 1009. Product of Polynomials (25)
    PAT 解题报告 1007. Maximum Subsequence Sum (25)
    PAT 解题报告 1003. Emergency (25)
    PAT 解题报告 1004. Counting Leaves (30)
    【转】DataSource高级应用
    tomcat下jndi配置
    java中DriverManager跟DataSource获取getConnection有什么不同?
    理解JDBC和JNDI
    JDBC
    Dive into python 实例学python (2) —— 自省,apihelper
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/9537192.html
Copyright © 2020-2023  润新知