欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-15 21:55:58
...

题目:

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

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

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

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

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

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

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

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

所以考虑贪心;
设红边长为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;
所以用链表链接每条线段;
注意边界需要赋值为正无穷,因为那里的线段不满足;
洛谷 3620 [APIO2007]数据备份 贪心

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

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;
}