洛谷 P3620 [APIO/CTSC 2007]数据备份 解题报告

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 对所需的网络电缆的最小总长度。


好题。

首先我们抽象一下,发现要配对一定要搞相邻的在一起。

于是问题可以变成,在(n-1)个数中选择(k)个数,使得这(k)个数两两不相邻,最大化它们的和

(n-1)个数指两个的距离,两两不相邻保证不会选重。

这个可以用DP做,斜率优化后可过,不过要二分斜率还要滚动数组。

这里主要说一说贪心的做法

我们尝试使用归纳法类似的方法

当只选一个时,直接选最小的

选两个时,要么选最小的和不是它两边的两个的剩下的最小的,要么选最小的两边的

如果我们每一步都面临这个子问题,就可做了

假设我们每步强制选出最小值,但我们给它一个反悔的机会,不就可以了吗

具体的即为,拿出最小值(C_{pos})统计答案,然后放入(C_{pos-1}+C_{pos+1}-C_{pos}),表示在后面的步数时可以反悔

这样,我们就可以保证取完每次后都是最优的了

位置关系用链表维护。

注意,端点的左边或者右边的答案设置正无穷,表示没有选的


Code:

#include <cstdio>
#include <queue>
#define ll long long
using namespace std;
const int N=100010;
int n,k,pre[N],suc[N],used[N];ll ans,dat[N];
struct node
{
    int pos;ll cost;
    bool friend operator <(node n1,node n2)
    {
        return n1.cost>n2.cost;
    }
};
priority_queue <node> q;
int main()
{
    scanf("%d%d",&n,&k);
    ll las,now;
    scanf("%lld",&las);
    for(int i=1;i<n;i++)
    {
        scanf("%lld",&now);
        pre[i]=i-1,suc[i]=i+1;
        dat[i]=now-las;
        node tt={i,now-las};
        q.push(tt);
        las=now;
    }
    dat[0]=dat[n]=0x3f3f3f3f3f3f3f3f;
    for(int i=1;i<=k;i++)
    {
        int pos=q.top().pos;ll c=q.top().cost;
        q.pop();
        if(used[pos]) {i--;continue;}
        ans+=c;
        dat[pos]=dat[pre[pos]]+dat[suc[pos]]-dat[pos];
        used[pre[pos]]=used[suc[pos]]=1;
        suc[pre[pre[pos]]]=pos,pre[pos]=pre[pre[pos]];
        pre[suc[suc[pos]]]=pos,suc[pos]=suc[suc[pos]];
        node tt={pos,dat[pos]};
        q.push(tt);
    }
    printf("%lld
",ans);
    return 0;
}


2018.8.8

原文地址:https://www.cnblogs.com/butterflydew/p/9445235.html