P1484 种树(后悔贪心+双向链表+优先队列)

题目传送门

题意

给定n个坑的种树利润,你现在有k个树种,且不能在相邻的两个坑中种树,问你最大的利润。

输入格式

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

输出格式

输出 11 个数,表示种树的最大利润。

数据范围

n<=500000.
k<=(n/2)

样例

input

6 3
100 1 -1 100 1 -1

output

200

思路

看到这题的瞬间,就会很明显的想到dp建立状态表示这个位置的坑有无种树,然后for一遍的dp更新,但是看了看数据范围显然是不可以的。
突破口还是在于相邻的两个位置不能种树,我们贪心的考虑,最大的利润的坑是位置x,我们先种上树,那么他相邻的两个坑x-1x+1显然是不能种了,我们更新一个新的坑表示如果取x-1x+1的时候的利润,也就是a[x+1]+a[x-1]-a[x],如果我们后来选了这个新的坑,表示我们其实反悔了,如果反悔了,那肯定同时选了两边,每次反悔相当于是多了(-1+2)个点,所以每次还是只多选了一棵树。
举个例子:n=10
假设我们现在选了a4这个坑,那我们不能选a3和a5,于是我们更新出新的坑为a11=a3+a5-a4,此时,我们只用了a4这个坑。
然后下一次反悔我们想选a11了,那我们就更新出再新的坑为a12=a2+a6-(a3+a5-a4),此时,我们就只用了a3和a5这两个坑。
如果这时候再想选a12了,新的坑为a13=a1+a7-(a2+a6-(a3+a5-a4)),此时我们用了的就是a2,a4,a6。(绝绝子)。
记得及时退出(也就是如果当前的坑是负收益,我们就不要再种了!)

code

#include <bits/stdc++.h>
using  namespace  std;

typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}

const ll INF=0x3f3f3f3f3f3f3f3f;//2147483647;
const int N=1e6+50,M=1e5+50;
const ll mod=998244353;


int n,k;
int l[N],r[N];
ll a[N];
priority_queue<PLL>q;
int vis[N];
void solve() {
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    a[0]=-INF,a[n+1]=-INF;
    for(int i=1;i<=n;i++){
        q.push({a[i],i});
        l[i]=i-1;
        r[i]=i+1;
    }
    int idx=n+1;
    ll ans=0;
    for(int i=1;i<=k;i++){
        while(!q.empty()&&vis[q.top().second]){
            q.pop();
        }
        if(q.empty()){
            break;
        }
        PLL u=q.top();q.pop();
        if(u.first<=0){
            break;
        }
        ans+=u.first;
        vis[u.second]=vis[l[u.second]]=vis[r[u.second]]=1;
        a[++idx]=a[l[u.second]]+a[r[u.second]]-u.first;
        l[idx]=l[l[u.second]];
        r[idx]=r[r[u.second]];
        r[l[idx]]=idx;
        l[r[idx]]=idx;
        q.push({a[idx],idx});
    }
    cout<<ans<<"\n";
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int __=1;//cin>>__;
    while(__--){
        solve();
    }
    return 0;
}

环形种树

区别

就是多了两个条件。
1.种树变成了环形,相当于1和n这两棵树相邻了。
2.必须将k个树苗全部用上。

解决方案

1.令l[1]=n,r[n]=1
2.不要及时退出了,负收益也得种,直到树苗用完呜呜。

code

#include <bits/stdc++.h>
using  namespace  std;

typedef long long ll;
typedef unsigned long long ull;
//#pragma GCC optimize(3)
#define pb push_back
#define is insert
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define show(x) cerr<<#x<<" : "<<x<<endl;
//mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count());
//ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);}

const ll INF=0x3f3f3f3f3f3f3f3f;//2147483647;
const int N=1e6+50,M=1e5+50;
const ll mod=998244353;


int n,k;
int l[N],r[N];
ll a[N];
priority_queue<PLL>q;
int vis[N];
void solve() {
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    if(n<k*2){
        cout<<"Error!";
        return ;
    }
    for(int i=1;i<=n;i++){
        q.push({a[i],i});
        l[i]=i-1;
        r[i]=i+1;
    }
    l[1]=n;r[n]=1;
    int idx=n+1;
    ll ans=0;
    for(int i=1;i<=k;i++){
        while(!q.empty()&&vis[q.top().second]){
            q.pop();
        }
        if(q.empty()){
            break;
        }
        PLL u=q.top();q.pop();
        ans+=u.first;
        vis[u.second]=vis[l[u.second]]=vis[r[u.second]]=1;
        a[++idx]=a[l[u.second]]+a[r[u.second]]-u.first;
        l[idx]=l[l[u.second]];
        r[idx]=r[r[u.second]];
        r[l[idx]]=idx;
        l[r[idx]]=idx;
        q.push({a[idx],idx});
    }
    cout<<ans<<"\n";
}

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int __=1;//cin>>__;
    while(__--){
        solve();
    }
    return 0;
}
posted @   illume  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示