[codevs1615]数据备份

本文介绍了一种解决线段选择问题的方法,该问题要求从n条线段中选择k条端点不重合且总长度最小的线段。通过使用链表和贪心策略,文章详细阐述了算法实现过程。

题目←

大意:
可以转换成n条线段中选k条端点不重合的线段,要求线段之和最小
发现一条线段选了之后会影响两边的,考虑链表
还有这样一条替代关系:

选择一条长度为v的线段后,两旁的线段不能再选,但若要重选两条线段,可将答案增加lv+rv-v来抵消
其中lv、rv为左右线段的价值

综上,选择一条价值为v的线段时,在链表空间中新加入lv+rv-v,代替v的位置
然后继续从小到大贪心就可以了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#define LL long long
#define INF 1061109567
using namespace std;
const int MAXN = 1000000 + 50;
LL next[MAXN],pre[MAXN],A[MAXN],D[MAXN];
struct zt{
    LL pos;
    LL cost,v;
}l[MAXN];
bool operator < (zt a,zt b){
    return a.v > b.v;
}
priority_queue <zt> q;
LL n,m,k,cnt;
int first;
LL tot,ans,now;
bool del[MAXN];
int main()
{
    scanf("%lld%lld",&n,&m);
    scanf("%lld",&D[1]);
    for(int i = 2;i <= n;i ++){
        scanf("%lld",&D[i]);
        A[++ cnt] = D[i] - D[i - 1];
    }
    for(int i = 1;i <= cnt;i ++){
        pre[i] = i - 1;
        next[i - 1] = i;
        q.push((zt){i,1,A[i]});
    }
    pre[0] = -1;
    next[cnt] = cnt + 1;
    first = 0;
    A[0] = INF;
    A[cnt + 1] = INF;
    cnt ++;
    while(!q.empty())
    {
        zt u = q.top();
        q.pop();
        if(del[u.pos])continue;
        tot += u.cost;
        now += u.v;
        if(tot == m){
            ans = now;
            break;
        }
        u.v = -u.v;
        u.v += A[pre[u.pos]] + A[next[u.pos]];
        A[++ cnt] = u.v;
        int tmp1 = pre[u.pos],tmp2 = next[u.pos];
        next[pre[tmp1]] = cnt;
        pre[next[tmp2]] = cnt;
        pre[cnt] = pre[tmp1];
        next[cnt] = next[tmp2];
        del[tmp1] = del[tmp2] = del[u.pos] = true;
        u.pos = cnt;
        q.push(u);
    }
    printf("%lld",ans);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值