【BZOJ 1150】 [CTSC2007]数据备份Backup

1150: [CTSC2007]数据备份Backup

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 797   Solved: 333
[ Submit][ Status]

Description

 

Input

输入的第一行包含整数n和k,其中n(2 ≤ n ≤100 000)表示办公楼的数目,k(1≤ k≤ n/2)表示可利用的网络电缆的数目。接下来的n行每行仅包含一个整数(0≤ s ≤1000 000 000), 表示每个办公楼到大街起点处的距离。这些整数将按照从小到大的顺序依次出现。

Output

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

Sample Input

5 2
1
3
4
6
12

Sample Output

4

HINT

上面的样例输入给出了前面描述的示例情形 对于每一个测试点,如果写到输出文件中的答案正确,则得到该测试点100%的分数,否则得零分。30%的输入数据满足n≤20。60%的输入数据满足n≤10 000。


贪心+堆。


首先对读入数据差分,题目就变成了:

从差分后的n-1个数中选出k个不相邻的数,使得他们的和最小。


对于“不相邻”的处理非常巧妙~


1.首先我们把所有的数放入堆中


2.取出最小的那个x


3.接下来把这条线段改成他左边的线段+右边的线段-x,插入堆中


4.把x左右两边的线段都删去


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


每次从堆中取出一个数,必然会使取出的总线段+1,并且满足都不相邻的条件。


贪心的来看,每次取出来的都是最小的,也就是使得取出的总线段数多一条的最小代价,因此满足总线段长度最小。


#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <queue>
#define inf 0x3f3f3f3f
#define mp make_pair
#define pa pair<int,int>
#define M 100000+5
using namespace std;
int n,m,len[M],pre[M],ne[M];
priority_queue<pa,vector<pa>,greater<pa> > q;
void read(int &tmp)
{
	tmp=0;
	char ch=getchar();
	int fu=1;
	for (;ch<'0'||ch>'9';ch=getchar())
		if (ch=='-') fu=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())
		tmp=tmp*10+ch-'0';
	tmp*=fu;
}
int main()
{
        read(n),read(m);
	int x=0,y;
	for (int i=1;i<=n;i++)
	{
		read(y);
		len[i]=y-x;
		pre[i]=i-1,ne[i]=i+1;
		x=y;
	}
	int ans=0;
	for (int i=2;i<=n;i++)
		q.push(mp(len[i],i));
	pre[2]=0,ne[n]=0;
	for (int i=1;i<=m;i++)
	{
		while (q.top().first!=len[q.top().second])
			q.pop();
		int x=q.top().second,l=pre[x],r=ne[x];
		ans+=len[x];
		q.pop();
		pre[ne[x]=ne[r]]=x;
		ne[pre[x]=pre[l]]=x;
		len[x]=l&&r?min(inf,len[l]+len[r]-len[x]):inf;
		len[l]=len[r]=inf;
		q.push(mp(len[x],x));
	}
	printf("%d\n",ans);
	return 0;
}



感悟:

1.这道题在思路上非常巧妙:

如果要取一条线段两端的线段,那么这条线段就不能存在,于是线段长度变成左边+右边-当前线段;


而这个思路对于多条线段也是成立的,我们可以把合并好的多条线段一起来看,如果当前线段是3条线段合并成的:a+b-c,那么左边+右边-当前线段的时候相当于删去a,b,然后把c“复活”


2.代码的写法也很巧妙(orz zyf-zyf)

对于一条线段,记录左边和右边的端点,pre[],ne[];删除一个点的时候,无法直接从队列中取出,那么把他的值赋为inf,从队列中取出的时候发现队列中的长度与这个值不符,则直接跳过。


3.UPD:其实直接用set就能实现了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值