洛谷 3620 [APIO2007]数据备份 贪心

18 篇文章 0 订阅

题目:

https://www.luogu.org/problemnew/show/3620

一道神题……
据说正解是由网络流分析得到的,蒟蒻瑟瑟发抖;

首先考虑O(n*k)的DP;
dp[i][j][0/1] : 前i条线段,选j条,是否以i结尾(结尾为1,否则为0);

显然100000是无法承受的,考虑如何O(nlogn)做这道题;

这里写图片描述

对于上图,我们要么选红边,要么选它两边的边,并且,两边的边一定同时选;

为什么?
考虑,若答案中不包含红边,只包含黑边黄边其中一条;
则可以用红边去替换它,答案更优,故黑边黄边必然同时选;

注意:黑边和黄边必然大于红边,原因在下面;

所以考虑贪心;
设红边长为x,黑边长为y1,黄边长为y2;
维护一个小根堆;
每次取出堆顶元素红边,它两边的黑边和黄边必然大于它;
然后ans=ans*+x;
将y1,x,y2合为一条新的线段y1+y2-x;
再将y1+y2-x丢进堆中;
如果选黄边和黑边更优,则后面y1+y2-x必然作为堆顶,也相当于”选择了两次”(选x时算一次,此时算一次,共两次);
则存在ans+=y1+y2-x;
ans=ans* +x+y1+y2-x=ans *+y1+y2;
此时相当于将x删去,选y1与y2;
所以用链表链接每条线段;
注意边界需要赋值为正无穷,因为那里的线段不满足;
这里写图片描述

假设黑色的是边界,黑<红<黄;
则选黑边一定比选红边优;
因为,选了黑边就不能选红边,对黄边无影响(即对后面的边影响小);
而选了红边就不能选黑边和黄边,对后面影响大,且不是最优解;
故将边界设为极大值,遇到类似情况必然选黑边而不会选红边,同时使得所有边均满足上面所说的性质;

WA的原因:
没有分清dis与a数组,变量名打错,mdzz;

简洁清晰的代码~~~~

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=100001+22;
int nxt[MAXN],pre[MAXN],dis[MAXN],a[MAXN];
int n,k,last,ans;
bool vis[MAXN];
struct hh {int pos,v;}u;
bool operator < (hh a,hh b) {return a.v > b.v;}
priority_queue<hh>q;

void solve()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&dis[i]);
    sort(dis+1,dis+n+1),last=dis[1];
    for(int i=2;i<=n;i++)
    {
        a[i]=dis[i]-last,last=dis[i];
        q.push((hh){i,a[i]}),nxt[i]=i+1,pre[i]=i-1;
    }
    a[1]=a[n+1]=1e9,nxt[n+1]=1,pre[1]=n+1;
    for(int i=1;i<=k;i++)
    {
        u=q.top(),q.pop();
        while(!q.empty() && vis[u.pos]) u=q.top(),q.pop();
        ans+=u.v;
        q.push((hh){u.pos,a[pre[u.pos]]+a[nxt[u.pos]]-a[u.pos]});
        a[u.pos]=a[pre[u.pos]]+a[nxt[u.pos]]-a[u.pos];
        nxt[pre[pre[u.pos]]]=u.pos,pre[nxt[nxt[u.pos]]]=u.pos;
        vis[nxt[u.pos]]=vis[pre[u.pos]]=true;
        nxt[u.pos]=nxt[nxt[u.pos]],pre[u.pos]=pre[pre[u.pos]];

    }
    cout<<ans<<'\n';
    return;
}

int main()
{
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值