題目連接:BZOJ - 1150php
能夠看出,咱們選的 k 條邊必定是相鄰兩點之間的線段。咱們能夠將每條邊當作一個點,那麼咱們就是要在 n-1 個點中選出互不相鄰的 k 個,使它們的和最小。ios
咱們使用一種神奇的貪心,開始的時候將全部的點權加入堆中,而後取 k 次,每次取權值最小的點,而後將這個點的點權加入答案中,咱們取了這個點以後就不能取與它相鄰的兩個點了,因此咱們要將這個點和相鄰兩點的權值都從堆中刪除。可是咱們最終的答案並不必定要取這個點,有可能咱們會不取這個點,而是取與它相鄰的兩個點。因此咱們在刪除了這個點和與它相鄰的兩個點以後,要在堆里加入一個新的點:權值是與這個點相鄰的兩個點的權值和-這個點的權值,若是以後取到這個新的點就至關於撤銷取這個點,而是取與它相鄰的兩個點,這樣取到的點數仍是加一的。spa
而後這樣處理以後咱們就認爲這個新建的點取代了以前的3個點的位置,而後與這3個點兩邊的點相鄰,須要用鏈表維護。blog
還有特殊的狀況,對於兩端的點,與它們相鄰的點只有1個,咱們要在第一個點以前和最後一個點以後分別加上一個權值爲 INF 的點,這樣進行處理以後新建的點的權值也必定很是大,以後就不會被取到。get
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <set> using namespace std; const int MaxN = 100000 + 5, INF = 999999999; int n, k, Ans; int A[MaxN], d[MaxN], Next[MaxN], Prev[MaxN]; struct ES { int x, y; ES() {} ES(int a, int b) {x = a; y = b;} bool operator < (const ES &e) const { if (y == e.y) return x < e.x; return y < e.y; } }; set<ES> S; set<ES> :: iterator It; int main() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { scanf("%d", &d[i]); A[i] = d[i] - d[i - 1]; } for (int i = 2; i <= n; ++i) { Next[i] = i + 1; Prev[i] = i - 1; S.insert(ES(i, A[i])); } A[1] = A[n + 1] = INF; int x; for (int i = 1; i <= k; ++i) { It = S.begin(); x = (*It).x; Ans += A[x]; S.erase(ES(x, A[x])); S.erase(ES(Prev[x], A[Prev[x]])); S.erase(ES(Next[x], A[Next[x]])); A[x] = A[Prev[x]] + A[Next[x]] - A[x]; S.insert(ES(x, A[x])); if (Prev[Prev[x]]) Next[Prev[Prev[x]]] = x; if (Next[Next[x]]) Prev[Next[Next[x]]] = x; Prev[x] = Prev[Prev[x]]; Next[x] = Next[Next[x]]; } printf("%d\n", Ans); return 0; }