数据备份

数据备份

在坐标轴上有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