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、有时在维护合并操作时,不一定要真正合并。

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

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

  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/newera/p/8977924.html
Copyright © 2011-2022 走看看