首页 > 其他 > 详细

数据备份

时间:2019-07-26 10:58:39      阅读:51      评论:0      收藏:0      [点我收藏+]

数据备份

在坐标轴上有n个点,第i个点的坐标为\(x_i\),现在请选出k对点(一个点不能同时在两对以上的点中),使每一对点的距离之和最小,求出这个最小值,\(2≤n≤100000,1≤k≤n/2\)

首先容易知道的结论是一对点必然选择的是相邻的,否则在最优解,将两个点变为相邻,结果会更加优秀。

将点的坐标从小到大排序(原题给出的就是有序的),处理出两个点间的距离,构成一个长度为n-1的数列\(\{a_i\}\),以后所说的n都为\(\{a_i\}\)的长度,现在问题转化成从这个数列中选出k个数,不相邻的和的最小值。

数列已经是有序的,于是我们不应该排序,最多是构造新序列,排好序,然后再构造一个序列对应其在原序列的位置,然后从小往大选,但是问题在于这个数不一定选,往后看就明白了

朴素的最优性贪心的思想,告诉我们要从最小的数\(a_i\)开始,但是它一定选吗?它选了之后,最终导致与之相邻的两个数不能选择,容易知道,决策要么是选这个数\(a_i\),要么是选\(a_{i-1},a_{i+1}\),因为如果\(a_{i-1},a_{i+1}\)只选一个,改成\(a_i\)会更加优秀,因此一个数字延伸多种决策,而这个数字又是最优秀的数字,考虑合并性贪心思想。

多种决策不妨将\(a_{i-1},a_i,a_{i+1}\)合并成一个数字,对应的值\(a_{i-1}+a_{i+1}-a_i\),此时答案累加\(a_i\),如果接下来选了\(a_{i-1}+a_{i+1}-a_i\),表示选了\(a_{i-1}+a_{i+1}\),否则表示选了\(a_i\),正好前者对应两次选择,后者对应一次选择,于是我们成功地将多种决策合并成一种决策,三个数字合并成一个数字。

因此按这样选择的k个数字,最好累加的答案,就是我们需要的,注意的是如果一个点没有两个点可以和它相邻,还是要删去所有与它相邻的点,表示这个点以后不会决策。

因此对于以上思路,我们只要用小根堆维护取出最小值,用链表维护数字的合并,让小根堆顺带记录其在链表上的位置,就可以支持快速合并和查询,最终时间复杂度\(O(klog(n))\),这是一道很棒的思维题,但是我想不到。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define Size 200100
using namespace std;
template<class free>
struct small{
    free a[Size];int n;
    il void push(free x){
        a[++n]=x;ri int p(n);
        while(p>1)
            if(a[p]<a[p>>1])
                swap(a[p],a[p>>1]),
                    p>>=1;else break;
    }
    il void pop(){
        a[1]=a[n--];ri int p(1),s(2);
        while(s<=n){
            if(s<n&&a[s+1]<a[s])++s;
            if(a[s]<a[p])
                swap(a[s],a[p]),p=s,s=p<<1;
            else break;
        }
    }
};
template<class free>
struct list{
    struct iter{
        iter *pre,*next;free v;
    }*head,*tail,*lt;
    il void initialize(){
        head=new iter(),tail=new iter();
        head->next=tail,tail->pre=head;
    }
    il void insert(iter*p,free v){
        lt=new iter{p->pre,p,v};
        p->pre->next=lt,p->pre=lt;
    }
    il void erase(iter*p){
        p->pre->next=p->next;
        p->next->pre=p->pre;
        p->v=-1;
    }
};
struct er{
    int d;list<int>::iter *p;
    il bool operator<(const er&x)const{
        return d<x.d;
    }
};
int s[Size];
list<int>L;small<er>S;
list<int>::iter *m,*l,*r;
il void read(int&);
int main(){
    int n,k,ans(0);
    read(n),read(k),L.initialize();
    for(int i(1);i<=n;++i)read(s[i]);
    --n;for(int i(1);i<=n;++i)
            s[i]=s[i+1]-s[i],L.insert(L.tail,s[i]),
                S.push((er){s[i],L.tail->pre});
    for(int i(1);i<=k;++i){
        while(m=S.a[1].p,m->v==-1)S.pop();
        l=m->pre,r=m->next,ans+=m->v;
        if(l==L.head&&r==L.tail)break;
        if(l==L.head)L.erase(r),L.erase(m);
        else if(r==L.tail)L.erase(l),L.erase(m);
        else{
            L.insert(r,l->v+r->v-m->v);
            L.erase(m),m=r->pre,S.push({m->v,m});
            L.erase(l),L.erase(r);
        }S.pop();
    }printf("%d",ans);
    return 0;
}
il void read(int &x){
    x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}

数据备份

原文:https://www.cnblogs.com/a1b3c7d9/p/11248339.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!