题目描述 Description
你在一家IT 公司为大型写字楼或办公楼(offices)的计算机数据做备份。
然而数据备份的工作是枯燥乏味的,因此你想设计一个系统让不同的办公楼彼此
之间互相备份,而你则坐在家中尽享计算机游戏的乐趣。
已知办公楼都位于同一条街上。你决定给这些办公楼配对(两个一组)。每
一对办公楼可以通过在这两个建筑物之间铺设网络电缆使得它们可以互相备份。
然而,网络电缆的费用很高。当地电信公司仅能为你提供K 条网络电缆,
这意味着你仅能为K 对办公楼(或总计2K 个办公楼)安排备份。任一个办公楼
都属于唯一的配对组(换句话说,这2K 个办公楼一定是相异的)。
此外,电信公司需按网络电缆的长度(公里数)收费。因而,你需要选择这
K 对办公楼使得电缆的总长度尽可能短。换句话说,你需要选择这K 对办公楼,
使得每一对办公楼之间的距离之和(总距离)尽可能小。
下面给出一个示例,假定你有5 个客户,其办公楼都在一条街上,如下图所
示。这5 个办公楼分别位于距离大街起点1km, 3km, 4km, 6km 和12km 处。电信
上例中最好的配对方案是将第1 个和第2 个办公楼相连,第3 个和第4 个办
公楼相连。这样可按要求使用K=2 条电缆。第1 条电缆的长度是3km―1km =
2km,第2 条电缆的长度是6km―4km = 2 km。这种配对方案需要总长4km 的网
络电缆,满足距离之和最小的要求。
输入描述 Input Description
输入的第一行包含整数n 和k,其中n(2 ≤ n ≤100 000)表示办公楼的数目,
k(1≤ k≤ n/2)表示可利用的网络电缆的数目。
接下来的n 行每行仅包含一个整数(0≤ s ≤1000 000 000), 表示每个办公楼
到大街起点处的距离。这些整数将按照从小到大的顺序依次出现。
输出描述 Output Description
输出应由一个正整数组成,给出将2K 个相异的办公楼连成k 对所需的网络
电缆的最小总长度。
样例输入 Sample Input
5 2
1
3
4
6
12
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
30%的输入数据满足n≤20。
60%的输入数据满足n≤10 000。
【2017.11.5考试T1】
正解是贪心。
/*
输入后排序
算出每条线段的长度 线段统一标号 将线段位置、1、长度放入小根堆
初始化链表 链表元素是边
初始化边界
堆顶依次弹出
弹出时顺便计算堆顶线段相邻两条线段的长度-堆顶,放入堆,备用
如果能用到,说明加上堆顶相邻的边比加堆顶的边更优 由于已经作差,所以相当于堆顶没有加入答案
加入备用的答案时,将堆顶左边相邻的线段的左边线段的nxt指向cnt,右边同理
更新cnt的pre和nxt
*/
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000000+10;
long long pre[maxn],nxt[maxn];
long long a[maxn],x[maxn];
bool vis[maxn];
long long n,k;
struct node
{
long long pos,s,len;
};
bool operator < (node a,node b)
{
return a.len>b.len;
}
priority_queueq;
int main()
{
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
long long cnt=0;
for(int i=1;i<=n-1;i++) x[++cnt]=a[i+1]-a[i];
for(int i=1;i<=cnt;i++)
{
pre[i]=i-1;
nxt[i-1]=i;
q.push((node){i,1,x[i]});
}
pre[0]=-1,nxt[cnt]=cnt+1;
x[0]=0x3f3f3f3f,x[cnt+1]=0x3f3f3f3f;
long long tot=0,ans=0;
cnt++;
while(!q.empty())
{
node f=q.top();
q.pop();
if(vis[f.pos]) continue;
tot+=f.s;
ans+=f.len;
if(tot==k) break;
f.len=-f.len;
f.len+=x[pre[f.pos]]+x[nxt[f.pos]];
x[++cnt]=f.len;
long long l=pre[f.pos],r=nxt[f.pos];
nxt[pre[l]]=cnt,pre[nxt[r]]=cnt;
nxt[cnt]=nxt[r],pre[cnt]=pre[l];
vis[l]=1,vis[r]=1,vis[f.pos]=1;
f.pos=cnt;
q.push(f);
}
printf("%lld\n",ans);
return 0;
}
考试把数据范围缩小了。dp可以过。
codevs只能45.听说滚动一下就能60了?
#include
#include
#include
#include
using namespace std;
const int maxn=500000+5;
int a[maxn],x[maxn],dp[1000][1000][2];
int n,k;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=n-1;i++) x[i]=a[i+1]-a[i];
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[1][0][0]=0,dp[1][1][1]=x[1];
for(int i=2;i<=n-1;i++)
{
for(int j=0;j<=min(i,k);j++)
{
dp[i][j][0]=min(dp[i-1][j][1],dp[i-1][j][0]);
if(j>0)
dp[i][j][1]=dp[i-1][j-1][0]+x[i];
}
}
int ans;
ans=min(dp[n-1][k][0],dp[n-1][k][1]);
printf("%d\n",ans);
return 0;
}