您的位置:首页 > 其它

[CTSC2007] 数据备份Backup

2016-03-24 21:30 197 查看
题意:给定n(2 <= n <= 100000)个点,从中选k(1 <= k <= n/2)对点,使得所有点对距离之和最小,每个数s都是0 <= s <= 1000000000,并且输入从小到大排序。

时间限制:10000ms

通过时间:488ms

分析:

我看到这题第一反应是dp,设f(i, j)表示前i个点,选j对,距离之和的最小值。

不难列出状态转移方程:f(i, j) = min(f(i-1, j), f(i-2, j-1) + a[i] - a[i-1]).

这样时空复杂度都是O(nk),空间开不下,观察到j只依赖j-1,所以可以滚动数组,提交后不幸发现TLE.

看来CTSC的题不是那么简单的,冥想半天不得其解,只好去网上寻找灵丹妙药。

进行一番搜索后,得出这道题做法如下:

首先由于每次选的点对必定是相邻的,所以我们可以求出n-1个相邻的差值, 原题变为,从n-1个数选出k个不相邻的数,使得这些数的和最小。

zrt把这道题归到了STL,可以用小根堆(优先队列)来做。

1.把所有差值放入堆中;

2.取出最小的值x;

3.把这个点改成它左边的点 + 右边的点 - x,插入堆中;

4.把x左右两边的点都删去;

5.返回到2,重复k次这个过程。

选左 + 右 - x表示不选x而选了x的左和右。

那,为什么这个是对的呢?

如果最终选了x,那么自然就是最优的,如果最终没有选x,那么一定会选x当前的两边,为什么呢?因为如果选了非x的两边,那么一定可以把另一边选成x,比当前解不会差,得证。

考虑一下实现方法,首先不仅要知道最小的值是多少,还要知道是第几个点,所以这里使用了一下pair,默认是按照第一维排序,于是第一维放差值,第二维放序号。

为了知道它左右的点,所以我要开两个数组pre和nxt分别表示前缀和后缀,初始化pre[2] = nxt
= 0,采用链表的思想进行更新。

删去操作并不一定要把它真的从队列里pop出来,我可以把它的值修改,然后第二步之前判断如果队列里的值和它实际的值不一样,说明已经选过了,pop()掉即可。

最后考虑一下它的边界,当我选第一个点或最后一个点的时候,显而易见可以证明选第二个点或第n-1个点绝对没有当前决策好,所以直接去除第一个点或第n-1个点。

既然是CTSC的题,可能会有点小坑,数的范围是0~10^9,所以inf要开大一点,我就是被这个坑到死...

dp代码(TLE):

#include <iostream>
#include <cstdio>
using namespace std;

int n, k, f, a[100005], d[100005][2];

int main() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int j = 1; j <= k; j++) {
f ^= 1;
for(int i = j*2; i <= n; i++) {
if(i > j * 2) d[i][f] = d[i-1][f];
else d[i][f] = 100000000;
d[i][f] = min(d[i][f], d[i-2][f^1] + a[i] - a[i-1]);
}
}
printf("%d", d
[f]);
return 0;
}


正解:代码很短,但是蕴含的东西很多。

#include <cstdio>
#include <queue>
using namespace std;

typedef pair<int, int> pr;
const int inf = 0x3f3f3f3f;
int n, k, x, y, ans, len[100005], pre[100005], nxt[100005];
priority_queue<pr, vector<pr>, greater<pr> > q;

int main() {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &x);
len[i] = x-y, y = x;
if(i != 1) q.push(make_pair(len[i], i));
pre[i] = i-1, nxt[i] = i+1;
}
pre[2] = 0, nxt
= 0;
while(k--) {
while(q.top().first != len[q.top().second]) q.pop();
int u = q.top().second, l = pre[u], r = nxt[u]; q.pop();
ans += len[u];
len[u] = l && r ? len[l] + len[r] - len[u] : inf;
pre[nxt[u] = nxt[r]] = u;
nxt[pre[u] = pre[l]] = u;
len[l] = len[r] = inf;
q.push(make_pair(len[u], u));
}
printf("%d\n", ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: