标签:游戏 插入 数据 整数 namespace operator clu 数学 就是
你在一家IT公司为大型写字楼或办公楼的计算机数据做备份。
然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此之间互相备份,而你则坐在家中尽享计算机游戏的乐趣。
已知办公楼都位于同一条街上,你决定给这些办公楼配对(两个一组)。
每一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份。
然而,网络电缆的费用很高。
当地电信公司仅能为你提供 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=2km。
这种配对方案需要总长 4km 的网络电缆,满足距离之和最小的要求。
第一行输入整数n和k,其中 n 表示办公楼的数目,k 表示可利用的网络电缆的数目。
接下来的n行每行仅包含一个整数s,表示每个办公楼到大街起点处的距离。
这些整数将按照从小到大的顺序依次出现。
5 2 1 3 4 6 12
4
二叉堆。
妙题,妙不可言。
同时,我一开始用指针写这题调了我2天还没调出来。(我吃柠檬呢我艹。然后就数组模拟指针就过了。
首先能想到的是肯定要相邻办公楼匹配。所以第一步先求出每两个相邻办公室间的距离,记住d1, d2, d3, …, d(n - 1)。那么问题就转化为:从D中选K个数使其之和最小,且相邻两个数不能被同时被选。
这题还是用问题缩放技巧。先想想K是最小数据规模的情况。当K = 1时,显然答案就是D中的min值。当K = 2时,显然答案有如下两种情况:
那么如果K = K呢?利用问题缩放技巧和数学归纳法,发现小规模规律是满足大规模数据的。于是我们设计出这么一种算法:先选择D中的min值,然后删除d(i - 1), d(i), d(i + 1),再插入d(i - 1) + d(i + 1) - d(i)到刚才的位置。然后求解“从新的D中选择K - 1个数”。
这么设计的话,如果取了d(i - 1) + d(i + 1) - d(i)这个数的话,就是上述第2种情况。没取到就是第1种情况。这恰好实现了我们上述的2种情况。
“选择D中min值”这一步可以用堆实现。但我偷懒就是set代替堆了。
#include <iostream>
#include <cstdio>
#include <set>
#define N 100005
#define int long long
using namespace std;
struct Node
{
int val, pos;
friend bool operator < (Node x, Node y) {
if(x.val == y.val) return x.pos < y.pos;
else return x.val < y.val;
}
};
int n, k, ans;
int d[N], pre[N], nex[N];
set<Node> st;
int read()
{
int x = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
return x;
}
signed main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++) d[i] = read();
for(int i = 1; i < n; i++) pre[i] = i - 1, nex[i] = i + 1;
pre[n] = n - 1, nex[0] = 1;
for(int i = 1; i < n; i++)
{
d[i] = d[i + 1] - d[i];
st.insert((Node){d[i], i});
}
d[0] = d[n] = (int)1e9;
for(int i = 1; i <= k; i++)
{
int pos = (*st.begin()).pos;
ans += d[pos], st.erase(st.begin());
st.erase((Node){d[pre[pos]], pre[pos]});
st.erase((Node){d[nex[pos]], nex[pos]});
d[pos] = d[pre[pos]] + d[nex[pos]] - d[pos];
st.insert((Node){d[pos], pos});
pre[pos] = pre[pre[pos]], nex[pos] = nex[nex[pos]];
nex[pre[pos]] = pos, pre[nex[pos]] = pos;
}
cout << ans;
return 0;
}
标签:游戏 插入 数据 整数 namespace operator clu 数学 就是
原文地址:https://www.cnblogs.com/BigYellowDog/p/11361329.html