zoukankan      html  css  js  c++  java
  • [P1484] 种树

    Link:https://www.luogu.org/problemnew/show/P1484

    Brief Introduction:在一个长为N上的序列取至多K个数,要保证所取的数两两不相邻,求Max(所取的数的和)。

                                   N<=5e5,K<=N/2

    Algorithm:

    1、首先O(NK)的dp是能立刻想到的 dp[n][k]=max(dp[n-1][k],dp[n-2][k-1]+value[n]) 

          可以使用滚动数组优化

          但明显不足以解决N<=5e5的问题

    2、可以从最简单的问题开始考虑,如果K=1,那么取最大值。设该点的位置为P。

          当问题扩大时,棘手的问题在于不易维护所取数两两不相邻这一条件

          为了每次选取最大值时可以忽略这个条件,我们选择P后将P-1,P,P+1合并,看为同一个点,这样保证接下来可以任意选点

          为了提供“反悔”这个选项,新点的权值设为value(P-1)+value(P+1)-value(P),如选择新点则表示“反悔”不选P,而改选P-1和P+1.

          维护数列中插入、删除、求最大值,可以想到使用堆/Priority_queue维护

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    inline int read()
    {
        char ch;int f=0,num;
        while(!isdigit(ch=getchar())) f|=(ch=='-');
        num=ch-'0';
        while(isdigit(ch=getchar())) num=num*10+ch-'0';
        return f?-num:num;
    }
    
    #define F first
    #define S second
    
    const int MAXN=5e5+10;
    typedef pair<int,int> P;
    typedef long long ll;
    priority_queue<P> Q;
    bool vis[MAXN];
    ll l[MAXN],r[MAXN],dat[MAXN];
    
    int main()
    {
        int n=read(),k=read();
        for(int i=1;i<=n;i++) 
        {
            dat[i]=read();
            l[i]=i-1;r[i]=i+1;  //维护每个点的两边的点的坐标
            Q.push(P(dat[i],i));
        }
        
        ll res=0;l[0]=1;r[n+1]=n; //对边界设置
        for(int i=1;i<=k;i++)
        {
            while(vis[Q.top().S]) Q.pop(); //如已被合并,则不处理
            P t=Q.top();Q.pop();
            if(t.F<0) break;
            res+=t.F;vis[l[t.S]]=vis[r[t.S]]=true;
            
            dat[t.S]=t.F=dat[l[t.S]]+dat[r[t.S]]-dat[t.S];
            Q.push(t);
            
            r[t.S]=r[r[t.S]];l[t.S]=l[l[t.S]];  //不真正合并,仅维护左右点坐标
            l[r[t.S]]=t.S;r[l[t.S]]=t.S;
        }
        cout << res;
        return 0;
    }

    Review:

    1、当遇到常见问题被加以特殊条件时,注意化归,通过转化将问题变为易解问题

       能否忽略特殊条件解题

    2、在决策类问题中,我们可以利用贪心+“反悔”选项的方式解决问题

         将权值变为 其他解-当前贪心解

    3、有时在维护合并操作时,不一定要真正合并。

          可将信息集中在其中一点上,只维护每个点相邻点坐标即可。

          同时,要对“废点”打上标记,之后对其不再处理

  • 相关阅读:
    深入理解Java中停止线程
    浅入浅出JDBC————1分钟了解JDBC
    Java多线程入门中几个常用的方法
    创建Java多线程的两种方式和线程异常
    小白学习前端---第二天 HTML的基本属性————1
    Info类
    Control类
    demo 代码
    防作弊原理
    状态类
  • 原文地址:https://www.cnblogs.com/newera/p/8977924.html
Copyright © 2011-2022 走看看