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

[BZOJ1150][CTSC2007]数据备份Backup(贪心+堆+链表)

程序员文章站 2022-03-31 21:18:18
...

传送门


  这题真是一环扣一环,做了好久好久,好题啊!
  首先通过题意我们可以敏锐的捕捉到:虽然可以跨楼相连,但是这样是没有意义的,因为相邻的两个点相连一定比跨楼相连更优。
  我们将楼与楼之间的距离求出来(n-1个数)那么问题就转化为:在n-1个数中选k个数使得他们相加起来最小,且选了一个数后相邻的两个数不可以再选。
  那也就是说,我们需要开一个小根堆维护最小值minn,如果选择了当前的最小值minn,那么最小值两边的值都不可以选,那么如果选了两边的反而更优呢??这里就把我难住了,其实啊,这题最精妙的部分就在于此。我们可以回想一下网络流“反向边退流”的思路,当我们发现这样子不优的时候可以通过反向边流回去,那么这题也与之同类,我们也可以设置一个“反向边”来反悔。
  那么如何设置反向边呢?我们往堆里加进一个新点q,设minn左边的点权值为prev,右边的点权值为next,那么q的权值就为prev+next-minn,为什么?如果我们在往后的决策中选到q这个元素,然后把它加进ans里面,那么我们之前加的minn的影响也随之删除了(minn+prev+next-minn=prev+next),取而代之的是minn两边的prev和next,成功完成了反悔的过程。
  具体过程:
  取出最小值1,ans+=1
  将他两边的2、3也取出,同时新加入一个4=2+3-1
  假如我们下次选到4,假设现在ans还是=1,那么ans=1+4=1+(2+3-1)=2+3,成功反悔。
  [BZOJ1150][CTSC2007]数据备份Backup(贪心+堆+链表)
  

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<vector>
using namespace std;
const int N=200010;
const int INF=(1<<30);
int n,k;
int pos[N];
struct heap
{
    int val,prev,next,k,pos;
    friend bool operator > (heap n1,heap n2){return n1.val>n2.val;}
}a[N]; int tot=0;
int w[N]; bool v[N];
priority_queue<heap , vector<heap> , greater<heap> >q;

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    a[0].prev=a[0].next=a[0].k=0; a[0].val=INF;
    for(int i=1;i<n;i++)
    {
        int x=w[i+1]-w[i];
        a[i].val=x;
        a[i].pos=i;
        if(i!=1) a[i].prev=i-1;
        if(i!=n-1) a[i].next=i+1;
        a[i].k=1;
        q.push(a[i]);
    }
    n--; tot=n;
    long long ans=0;
    memset(v,false,sizeof(v));
    while(k>0)
    {
        heap minn=q.top();
        q.pop();
        int pos=minn.pos,prev=a[pos].prev,next=a[pos].next;
        if(v[pos]) continue;
        v[pos]=v[prev]=v[next]=true;
        ans+=a[pos].val;
        k-=a[pos].k;
        //push(q=prev+next-p)
        tot++;
        a[tot].pos=tot; 
        a[tot].val=a[prev].val+a[next].val-a[pos].val;
        a[tot].k  =a[prev].k + a[next].k - a[pos].k;
        a[tot].prev=a[prev].prev; a[tot].next=a[next].next;
        a[a[prev].prev].next=tot;
        a[a[next].next].prev=tot;
        q.push(a[tot]);
    }
    printf("%lld\n",ans);
    return 0;
}