[BZOJ 1150] [CTSC2007] 數據備份Backup 【貪心 + 鏈表】

題目連接: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;
}
相關文章
相關標籤/搜索