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

1150: [CTSC2007]数据备份BackupTime Limit:10 SecMemory Limit:162 MBSubmit:797Solved: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 2134612

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+5using 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,从队列中取出的时候发现队列中的长度与这个值不符,则直接跳过。

终究还是会从指缝中一滴一滴流淌干净。

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

相关文章:

你感兴趣的文章:

标签云: