数据备份[APIO/CTSC 2007]题解

题目描述

你在一家IT公司为大型写字楼或办公楼的计算机数据做备份。

然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣。

已知办公楼都位于同一条街上,你决定给这些办公楼配对(两个一组)。

每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份。

然而,网络电缆的费用很高。

当地电信公司仅能为你提供 K 条网络电缆,这意味着你仅能为 K 对办公楼(总计2K个办公楼)安排备份。

任意一个办公楼都属于唯一的配对组(换句话说,这 2K 个办公楼一定是相异的)。

此外,电信公司需按网络电缆的长度(公里数)收费。

因而,你需要选择这 K 对办公楼使得电缆的总长度尽可能短。

换句话说,你需要选择这 K 对办公楼,使得每一对办公楼之间的距离之和(总距离)尽可能小。

下面给出一个示例,假定你有 5 个客户,其办公楼都在一条街上,如下图所示。
数据备份[APIO/CTSC 2007]题解_第1张图片

这 5 个办公楼分别位于距离大街起点 1km, 3km, 4km, 6km 和 12km 处。

电信公司仅为你提供 K=2 条电缆。

上例中最好的配对方案是将第 1 个和第 2 个办公楼相连,第 3 个和第 4 个办公楼相连。

这样可按要求使用 K=2 条电缆。

第 1 条电缆的长度是 3km-1km=2km ,第 2 条电缆的长度是 6km-4km=2km。

这种配对方案需要总长 4km 的网络电缆,满足距离之和最小的要求。

输入格式

第一行输入整数n和k,其中 n 表示办公楼的数目,k 表示可利用的网络电缆的数目。

接下来的n行每行仅包含一个整数s,表示每个办公楼到大街起点处的距离。

这些整数将按照从小到大的顺序依次出现。

输出格式

输出应由一个正整数组成,给出将2K个相异的办公楼连成k对所需的网络电缆的最小总长度。

样例输入

5 2
1
3
4
6
12

样例输出

4

数据范围与提示

2≤n≤100000,

1≤k≤n/2,

0≤s≤1000000000


开始做题啦!!!

Frist:思路

首先,因为这道题求的是最小距离和,所以我们要确保每次选的都是当前的最短距离,于是我们就想到了优先队列,每次取出堆顶,将堆顶加入累加和中;

但很快我们就发现题目不可能这么简单,如果最小的距离被选择,那么其相邻的两个距离将不能被选择

换言之,我们会有两种选择方式:

  1. 选择最小的距离,再从剩下的当中选择去除相邻两距离的最小值;
    即:选择d[i],丢弃d[i-1],d[i+1];
  2. 选择最小距离的相邻两个距离;
    即:选择d[i-1] and d[i+1],丢弃d[i];
    但因为我们不知道他会用第一还是第二种方案,所以我们就默认他用第一种方案,然后将d[i]赋值为d[i-1]+d[i+1]-d[i];

证明如此赋值的方法是正确的:

首先,根据我们的复制原则,d[i]已经进入了ans,那么如果d[i-1]+d[i+1]-d[i]也进入ans的话,和前面的d[i]一结合,其实ans里面放的就是d[i-1]和d[i+1]的值了然后因为我们输入了两次,刚好和d[i-1],d[i+1]的输入次数一样,所以就证毕啦!!!
代码如下:

#include
using namespace std;

struct zz{
	int index,v;
	friend bool operator  < (zz x,zz y){    //重载运算符 
		return x.v>y.v;
	}
};

priority_queue<zz> q;

int n,k;
int l[100005],r[100005],dp[100005]={};
bool f[100005];
long long ans=0;

int main(){
	cin>>n>>k;
	int x,y;
	cin>>x;                 //优先输入第一位,因为第一位和距离没关系 
	for(int i=1;i<n;i++){
		cin>>y;
		dp[i]=y-x;
		q.push({i,dp[i]});
		l[i]=i-1;
		r[i]= i+1==n?0:i+1;       	/*如果现在输入的是最后一位,那么它没有后继,
		所以把它的后继设为0,再将0设为无穷大;*/ 
		x=y; 
	}
	dp[0]=0x3f3f3f3f;  	//设为无穷大 ; 
	while(k){
		zz now=q.top();
		q.pop();
		int i=now.index;
		if(f[i])
			continue;     	//如果它相邻的距离被提取过,它就不可以被提取 ; 
		f[l[i]]=1;			
		f[r[i]]=1;			//把它的相邻的距离标记 ; 
		ans+=dp[i];			//加入总和 ; 
		dp[i]=dp[l[i]]+dp[r[i]]-dp[i];	//赋值; 
		q.push({i,dp[i]}); 	//放入堆; 
		l[i]=l[l[i]];		
		r[i]=r[r[i]];		//因为i+1和i-1不能使用,所以把i+1治为i+2,i-1同理; 
		r[l[i]]=i;
		l[r[i]]=i;			//反向操作,i+2的相邻距离由i+1变为i,i-2同理; 
		k--;
	}
	cout<<ans;
}

你可能感兴趣的:(贪心,思维,STL)