洛谷P3620 [APIO/CTSC 2007] 数据备份 [堆,贪心,差分]

  题目传送门

  

题目描述

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

已知办公楼都位于同一条街上。你决定给这些办公楼配对(两个一组)。每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份。

然而,网络电缆的费用很高。当地电信公司仅能为你提供 K 条网络电缆,这意味着你仅能为 K 对办公楼(或总计 2K 个办公楼)安排备份。任一个办公楼都属于唯一的配对组(换句话说,这 2K 个办公楼一定是相异的)。

此外,电信公司需按网络电缆的长度(公里数)收费。因而,你需要选择这 K对办公楼使得电缆的总长度尽可能短。换句话说,你需要选择这 K 对办公楼,使得每一对办公楼之间的距离之和(总距离)尽可能小。

下面给出一个示例,假定你有 5 个客户,其办公楼都在一条街上,如下图所示。这 5 个办公楼分别位于距离大街起点 1km, 3km, 4km, 6km 和 12km 处。电信公司仅为你提供 K=2 条电缆。

上例中最好的配对方案是将第 1 个和第 2 个办公楼相连,第 3 个和第 4 个办公楼相连。这样可按要求使用 K=2 条电缆。第 1 条电缆的长度是 3km―1km = 2km,第 2 条电缆的长度是 6km―4km = 2 km。这种配对方案需要总长 4km 的网络电缆,满足距离之和最小的要求。

输入输出格式

输入格式:

输入文件的第一行包含整数 n 和 k,其中 n(1≤n≤100 000)表示办公楼的数目,k(1≤k≤n/2)表示可利用的网络电缆的数目。

接下来的 n 行每行仅包含一个整数(0≤s≤1000 000 000), 表示每个办公楼到大街起点处的距离。这些整数将按照从小到大的顺序依次出现。

输出格式:

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

输入输出样例

输入样例#1: 复制

5 2
1
3
4
6
12 

输出样例#1: 复制

4

说明

30%的输入数据满足 n≤20。

60%的输入数据满足 n≤10 000



  分析:易证,最优解中配对的大楼一定是相邻的,就可以将每两栋相邻的大楼之间的距离Di求出来,就可以将问题转化为:[有n-1个元素的数列Di,从中取出k个数,使得这k个数的和最小,并且相邻的数不能同时选]。

  再从简单的情况开始分析:

  如果k=1,那么直接选最小的元素。

  如果k=2,那么要么选最小的元素D[i]和除了D[i-1],D[i],D[i+1]以外的最小的元素,要么选择D[i]两边的元素即D[i-1]和D[i+1]

  那么由第二种情况就可以分析得到,对于当前数列中的元素,要么一定要选最小元素D[i],要么就一定要选最小元素两边的元素D[i-1],D[i+1]。也就是说,最小元素左右两侧的元素要么同时被选,要么同时不选。。那么就可以先选出D[]中最小的元素D[i],然后将D[i-1],D[i],D[i+1]从数列中删除,并在原位置插入一个新元素D[i-1]+D[i+1]-D[i],然后重复操作即可。(因为如果在后面的操作中选择了D[i-1]+D[i+1]-D[i]这个元素,就相当于去掉已选择的结果中的D[i]换上D[i-1]+D[i+1],如果后面没选该元素,则选择D[i]是一步最优策略)

  那算法也就可以推导出了:建立一个链表L和一个小根堆H,L中有n-1个元素,每个元素与小根堆中的每一个元素成一一映射关系,它们的元素都是D[i],也就是每两栋楼之间的距离。取出堆顶,将权值累加到答案上,再将堆顶x删除,同时将x的左右两侧的元素pre[x],nxt[x]删除,在同样的位置插入一个新元素p,p的权值D[p]=D[pre[x]]+D[nxt[x]]-D[x],再在链表的对应位置中插入对应的节点p。重复k次即可。

  不过实际操作的时候有很多细节要注意,具体细节看代码吧。

  Code:

  

 1 //It is made by HolseLee on 10th May 2018
 2 //Luogu.org P3620
 3 #include<bits/stdc++.h>
 4 #define Fi(i,a,b) for(int i=a;i<=b;i++)
 5 using namespace std;
 6 const int N=2e5+7;
 7 int n,k,size,heap[N],fheap[N];
 8 int num[N],nxt[N],pre[N],ans;
 9 inline int read()
10 {
11   char ch=getchar();int num=0;bool flag=false;
12   while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)flag=true;ch=getchar();}
13   while(ch>=‘0‘&&ch<=‘9‘){num=num*10+ch-‘0‘;ch=getchar();}
14   return flag?-num:num;
15 }
16 inline void delet(int x)
17 {
18   heap[x]=heap[size--];
19   fheap[heap[size+1]]=x;
20   int ka=x;
21   if(num[heap[ka]]>=num[heap[ka>>1]]){
22     int s=ka<<1;
23     while(s<=size){
24       if(s<size&&num[heap[s+1]]<num[heap[s]])s++;
25       if(num[heap[ka]]>num[heap[s]]){
26     swap(fheap[heap[ka]],fheap[heap[s]]);
27     swap(heap[ka],heap[s]);ka=s;s=ka<<1;}
28       else break;}}
29   else{while(ka>1){if(num[heap[ka]]<num[heap[ka>>1]]){
30     swap(fheap[heap[ka]],fheap[heap[ka>>1]]);
31     swap(heap[ka],heap[ka>>1]);ka>>=1;}
32       else break;}}
33 }
34 inline void insert(int x)
35 {
36   heap[++size]=x;fheap[x]=size;int ka=size;
37   while(ka>1){
38     if(num[heap[ka]]<num[heap[ka>>1]])
39       {swap(fheap[heap[ka]],fheap[heap[ka>>1]]);
40     swap(heap[ka],heap[ka>>1]);ka>>=1;}
41     else break;}
42 }
43 int main()
44 {
45   n=read();k=read();int x1,x2;
46   x1=read();Fi(i,2,n){
47     nxt[i]=i+1;pre[i]=i-1;x2=read();
48     num[i]=x2-x1;x1=x2;insert(i);}
49   num[1]=1e9+7;num[++n]=1e9+7;
50   insert(1),insert(n);
51   Fi(i,1,k){
52     int x=heap[1];delet(1);
53     delet(fheap[pre[x]]);delet(fheap[nxt[x]]);
54     int x1=num[pre[x]],x2=num[nxt[x]];
55     num[++n]=x1+x2-num[x];
56     pre[n]=pre[pre[x]];nxt[n]=nxt[nxt[x]];
57     pre[nxt[n]]=n;nxt[pre[n]]=n;insert(n);
58     ans+=num[x];}cout<<ans;return 0;
59 }

原文地址:https://www.cnblogs.com/cytus/p/9021464.html

时间: 2024-10-31 21:14:52

洛谷P3620 [APIO/CTSC 2007] 数据备份 [堆,贪心,差分]的相关文章

P3620 [APIO/CTSC 2007] 数据备份

P3620 [APIO/CTSC 2007] 数据备份 题目描述 你在一家 IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份.然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣. 已知办公楼都位于同一条街上.你决定给这些办公楼配对(两个一组).每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份. 然而,网络电缆的费用很高.当地电信公司仅能为你提供 K 条网络电缆,这意味着你仅能为 K 对办公

解题:APIO/CTSC 2007 数据备份

题面 用双向链表把相邻两项的差串起来,用大根堆维护价值,每次贪心取最大的$x$.取完之后打标记删掉$pre[x]$和$nxt[x]$,之后用$val[pre[x]]+val[nxt[x]]-val[x]$替换这个$x$塞进堆里去,注意边界要连上一个极值 1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6

[APIO/CTSC 2007]数据备份

嘟嘟嘟 这竟然是一道贪心题,然而我在不看题解之前一直以为是dp. 首先最优的配对一定是相邻两个建筑物配对,所以我们求出差分数组,就变成了在n - 1个数中选出不相邻的k个数,使这k个数的和最小. 贪心是在回事呢?首先把所有点放在一个小根堆中,然后如果取出一个点ai,就把ai-1 + ai+1 - ai放到小根堆中,这样如果以后选了ai-1 + ai+1 - ai这个数,就把前面选的ai抵消了,所以这两次操作就相当于选了ai-1和ai+1这两个数. 每选一次就合并了两个数,那么进行k次就选了k个数

洛谷P3954 成绩【民间数据】

题目背景 数据已修复 题目描述 牛牛最近学习了C++入门课程,这门课程的总成绩计算方法是: 总成绩=作业成绩×20%+小测成绩×30%+期末考试成绩×50% 牛牛想知道,这门课程自己最终能得到多少分. 输入输出格式 输入格式: 输入文件只有1行,包含三个非负整数A.B.C,分别表示牛牛的作业成绩.小测成绩和期末考试成绩.相邻两个数之间用一个空格隔开,三项成绩满分都是100分. 输出格式: 输出文件只有1行,包含一个整数,即牛牛这门课程的总成绩,满分也是100分. 输入输出样例 输入样例#1: 复

洛谷 P1182 数列分段Section II Label:贪心

题目描述 对于给定的一个长度为N的正整数数列A[i],现要将其分成M(M≤N)段,并要求每段连续,且每段和的最大值最小. 关于最大值最小: 例如一数列4 2 4 5 1要分成3段 将其如下分段: [4 2][4 5][1] 第一段和为6,第2段和为9,第3段和为1,和最大值为9. 将其如下分段: [4][2 4][5 1] 第一段和为4,第2段和为6,第3段和为6,和最大值为6. 并且无论如何分段,最大值不会小于6. 所以可以得到要将数列4 2 4 5 1要分成3段,每段和的最大值最小为6. 输

[CTSC2007][APIO2007]数据备份Backup

题目:BZOJ1150.codevs1615.洛谷P3620 题目大意:有n个点,k条链,每个点离原点有一定的距离.要你用k条链连接2k个点,使得k条链的长度最短. 解题思路:毕竟是CTSC级别的题目,很难找出正确算法.在网上翻阅了很多资料后,终于理解了此题的正确算法.orz 正确算法为:贪心+链表+堆. 首先每次肯定是链相邻的2个点,所以我们先把相邻2个点的差值求出来,得到有n-1个数的数列. 然后问题就变成“在这个数列中寻找k个互不相邻的点,使得它们的和最小”. 我们把所有的数扔进一个堆里,

洛谷P1080 国王游戏 高精度 贪心 数学推公式

洛谷P1080 国王游戏        数学推公式      高精度    贪心 然而这并不是我打出来的,抄题解... 将左手与右手的乘积从小到大排序,然后计算求最大值即可.(需要高精度) 证明: 1)知道,如果相邻的两个人交换位置,只会影响到这两个人的值,不会影响他人 2)假设相邻的两个人i, i + 1.设A[i] B[i] <= A[i + 1] B[i + 1],i之前所有人的左手乘积为S. 则,ans1 = max{S / B[i], S * A[i] / B[i + 1]} 若交换

洛谷 P1005 矩阵取数游戏

题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2.每次取走的各个元素只能是该元素所在行的行首或行尾: 3.每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号): 4.游戏结束总得分为m次取数得分之和. 帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分. 输入输

洛谷1098 字符串的展开 解题报告

洛谷1098 字符串的展开 本题地址:http://www.luogu.org/problem/show?pid=1098 题目描述 在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或者“4-8”的字串,我们就把它当作一种简写,输出时,用连续递增的字母获数字串替代其中的减号,即,将上面两个子串分别输出为“defgh”和“45678”.在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活.具体约定如下:(1) 遇到下面的情