題目鏈接
題目描述
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個數中選k個,任意兩個不能相鄰,使得k個數的和最小。如果每次都選最小的,將它相鄰的刪掉,這樣貪心是有問題的。比方說100,2,1,2中選兩個,我們會選1和100。而最優方案爲兩個2。爲什麼會有這種情況?當我們選了第x個數時,會對第x+1和x-1個數產生影響。有可能選x-1和x+1而不選x會更優(一定是x-1和x+1同時選)。我們可以這樣調整。選了第x個數後將a[x-1]+a[x+1]-a[x]加入,並刪除x,x-1,x+1。這樣當我們選了這個數就意味着選第x+1,x-1個數而不選x。只有維護一個前驅後繼,再用堆來做就行了。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
#define mp make_pair
#define N 100010
#define se second
typedef pair<int,int>P;
priority_queue<P> h;
P tmp;
int n,k,ans,a[N],pre[N],suc[N];
char BUF[200001],*buf,*end;
#define getch() (buf==end?fread(BUF,1,200000,stdin),buf=BUF,end=buf+200000,*(buf++):*(buf++))
inline void read(int &x){
static char c;
for(c=getch();c<'0'||c>'9';c=getch());
for(x=0;'0'<=c&&c<='9';c=getch())x=x*10+c-'0';
}
int main(){
read(n); read(k); n--;
for(int i=1;i<=n+1;i++) read(a[i]);
for(int i=1;i<=n;i++) a[i]=a[i+1]-a[i];
for(int i=1;i<n;i++) pre[i]=i+1;
for(int i=2;i<=n;i++) suc[i]=i-1;
for(int i=1;i<=n;i++) h.push(mp(-a[i],i));
for(int i=1;i<=k;i++){
while(-h.top().first!=a[h.top().se]) h.pop();
tmp=h.top(); h.pop();
ans-=tmp.first;
if(pre[tmp.se]&&suc[tmp.se]){
a[tmp.se]=a[pre[tmp.se]]+a[suc[tmp.se]]-a[tmp.se];
a[pre[tmp.se]]=a[suc[tmp.se]]=1000000007;
pre[tmp.se]=pre[pre[tmp.se]];
suc[tmp.se]=suc[suc[tmp.se]];
suc[pre[tmp.se]]=pre[suc[tmp.se]]=tmp.se;
h.push(mp(-a[tmp.se],tmp.se));
} else{
a[pre[tmp.se]]=a[suc[tmp.se]]=1000000007;
pre[tmp.se]=pre[pre[tmp.se]];
suc[tmp.se]=suc[suc[tmp.se]];
suc[pre[tmp.se]]=pre[suc[tmp.se]]=0;
}
}
printf("%d\n",ans);
return 0;
}