标签:
已知有N座办公楼位于同一条街上。你决定给这些办公楼配对(两个一组)。每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备 份。然而,网络电缆的费用很高。当地电信公司仅能为你提供K条网络电缆,这意味着你仅能为K对办公楼(或总计2K个办公楼)安排备份。任一个办公楼都属于 唯一的配对组(换句话说,这2K个办公楼一定是相异的)。 此外,电信公司需按网络电缆的长度(公里数)收费。因而,你需要选择这K对办公楼使得电缆的总长度尽可能短。换句话说,你需要选择这K对办公楼,使得每一 对办公楼之间的距离之和(总距离)尽可能小。
输入的第一行包含整数n和k,n表示办公楼的数目,k表示可利用的网络电缆的数目。
接下来的n行每行包含一个整数s, 表示每个办公楼到大街起点处的距离。这些整数将按照从小到大的顺序依次出现。
一个正整数,表示将2K个相异的办公楼连成k对所需的网络电缆的最小总长度。
5 2 1 3 4 6 12
4
way:
贪心+堆。
首先对读入数据差分,题目就变成了:
从差分后的n-1个数中选出k个不相邻的数,使得他们的和最小。
对于“不相邻”的处理非常巧妙~
1.首先我们把所有的数放入堆中
2.取出最小的那个x
3.接下来把这条线段改成他左边的线段+右边的线段-x,插入堆中
4.把x左右两边的线段都删去
5.返回到2,重复k次这个过程
每次从堆中取出一个数,必然会使取出的总线段+1,并且满足都不相邻的条件。
贪心的来看,每次取出来的都是最小的,也就是使得取出的总线段数多一条的最小代价,因此满足总线段长度最小。
好吧。这解析根本是别人写的。知道就行了。写这题是为了写生日礼物(BZOJ 2288)那题的。
这是基本思想。巧妙运用的话。还是见生日礼物吧。
code:(相当优美经典的写法)
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <set> using namespace std; const int MaxN = 100000 + 5, INF = 999999999; int n, k, Ans; int A[MaxN], d[MaxN], Next[MaxN], Prev[MaxN]; struct ES { int x, y; ES() {} ES(int a, int b) {x = a; y = b;} bool operator < (const ES &e) const { if (y == e.y) return x < e.x; return y < e.y; } }; set<ES> S; set<ES> :: iterator It; int main() { ios::sync_with_stdio(false); cin>>n>>k; for (int i = 1; i <= n; ++i) { cin>>d[i]; A[i] = d[i] - d[i - 1]; } for (int i = 2; i <= n; ++i) { Next[i] = i + 1; Prev[i] = i - 1; S.insert(ES(i, A[i])); } A[1] = A[n + 1] = INF; int x; for (int i = 1; i <= k; ++i) { It = S.begin(); x = (*It).x; Ans += A[x]; S.erase(ES(x, A[x])); S.erase(ES(Prev[x], A[Prev[x]])); S.erase(ES(Next[x], A[Next[x]])); A[x] = A[Prev[x]] + A[Next[x]] - A[x]; S.insert(ES(x, A[x])); if (Prev[Prev[x]]) Next[Prev[Prev[x]]] = x; if (Next[Next[x]]) Prev[Next[Next[x]]] = x; Prev[x] = Prev[Prev[x]]; Next[x] = Next[Next[x]]; } cout<<Ans<<endl; return 0; }
标签:
原文地址:http://www.cnblogs.com/cherry231/p/5292943.html