《算法竞赛进阶指南》数据备份

数据备份

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

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

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

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

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

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

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

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

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

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

下面给出一个示例,假定你有 5 个客户,其办公楼都在一条街上,如下图所示。

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

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

1111.png

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

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

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

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

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

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

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

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

数据范围
2≤n≤100000,
1≤k≤n/2,
0≤s≤1000000000
输入样例:
5 2
1
3
4
6
12
输出样例:
4

本题简言之就是让我们从n条线路中寻找k条不相邻的的线路,并且使得k条线路总和最短。

这里我们用贪心的想法做:当我们当前寻找到最短线路(处于能被选择状态)后,我们要做的是,包括当前位置和其相邻的总共三个数据都将标记为“不可被选择状态”。

我们还要做的一个重要操作是,在我们以后的操作中通过整体判断可能会破环上述结构,我们就需要把破坏后的状态在放入到整体的循环中去,以便进行判断。

上述结构被破坏的话一定会变成“被选择状态(p-1) + 不能被选择状态( p ) + 被选择状态(p+1)”;这里就不加以证明了。

#include 
#include 
using namespace std;
typedef long long LL;
typedef pair<LL,int > PLI;
const int N=100010;
LL d[N];//m用差分数组表示两个楼层之间的距离
int l[N],r[N];//当一个“空隙”选择时,会使其相邻的两个“空隙”不能被选
//这里用“哨兵”表示不能被选择的范围
int n,k;
void delete_node(int p)
{//更新当前位置哨兵值的含义是,当当前处于不能被选择的状态的话

    r[l[p]]=r[p];//左边的哨兵位置“l[p]”元素的右边哨兵“r[l[p]]”本来应该是当前位置p,现在更新成右边哨兵位置r[p]
    l[r[p]]=l[p];//本语句解释与上句对应	

}
int main()
{
	ios::sync_with_stdio(false);
    cin>>n>>k;
    for(int i=0;i<n;i++)
    	cin>>d[i];
    for(int i=n-1;i;i--)
    	d[i]-=d[i-1];//建立差分数组
    d[0]=d[n]=1e15;//超过我们边界的线路我们设置为最大值,不去选择它们

    set<PLI>S;//用于存储所有的“空隙”和位置
    for(int i=0;i<n;i++)
    {//初始化“哨兵”,并将“空隙”的信息插入set中
      l[i]=i-1;
      r[i]=i+1;
      S.insert({d[i],i});
    }
    LL res=0;
    while(k--)
    {//寻找k条线路
      auto it=S.begin();//找到当前所有线路中最短的一条
      LL v=it->first;
      int p=it->second;
      int left=l[p],right=r[p];//获得两个哨兵
      S.erase(it);
      delete_node(left);
      delete_node(right);//选中当前位置的线路会影响两个哨兵位置的哨兵值

      res+=v;   
      S.erase({d[left],left});
      S.erase({d[right],right});//不仅相邻两个位置的哨兵值要跟新
      //在路线排序中也要删除,使得当前位置的相邻位置的元素处于不可被选择状态
      d[p]=d[right]+d[left]-d[p];
      //当如果要破坏当前状态的话(使得结果更优的话)。即“不能被选择状态(p-1) + 被选择状态(p) + 不能被选择状态(p+1)”
      //一定会变成“被选择状态(p-1) + 不能被选择状态(p) + 被选择状态(p+1)”
      //因此我们把这个可能出入进去,供我们以后选择
      S.insert({d[p],p});
    }
    cout<<res<<endl;
    return 0;
}

你可能感兴趣的:(《算法竞赛进阶指南》,贪心)