poj 1821 - dp,單調隊列

終於作出這題了!淚流滿面啊。

首先,要理解清楚題目意思。有一道線性籬笆由N個連續的木板組成。你手上有K個工人,你要叫他們給木板塗色。每一個工人有3個字段:L 表示 這個工人能夠塗的最大木板數目,S表示這個工人站在哪一塊木板,P表示這個工人每塗一個木板能夠獲得的錢。要注意的是,工人i能夠選擇不塗任何木板,不然,他的塗色區域必須是連續的一段,而且S[i]必須包含在內。 最後還有,每塊木板只能被塗一次。

理解完題目意思後,很容易聯想到郵局模型。因此看看能不能用相似的方程解決。
設f(i,j)表示使用前i個工人來對前j個木板進行塗色。
由這題,學到最重要 經驗就是,必定要把every細節都想清楚纔開始code,否則自找麻煩。
先看看咱們要計算哪些狀態,也就是每一維的值域。對於i,範圍應該是[0...K],對於每一個i,j的範圍應該是[0....N]
立刻能夠看到邊界條件是 當i == 0或者 j==0的時候,f(i,j)=0

而後如今假設 i 和 j 都不爲0。
若是j < s[i] , 顯然,這時候f(i,j) = f(i - 1,j)
若是j >= s[i],而且 j - s[i] + 1 < L[i]。這種狀況,表示的是,工人先能夠從木板s[i]開始向右連續 j - s[i] + 1塗個木板,而後工人能夠決定向左塗0...L[i] - (j -s[i] +1)個木板,因此,這個時候的轉移方程爲:

(#) f(i,j) = f(i-1,j) 或者 min { f(i-1,t) + P[i] * (s[i] - (t + 1) + 1) } + P[i] *(j -(s[i] + 1) + 1)

對於某個元組(i,j,t),這個方程的含義是,先由前i-1個工人給前t個木板塗色,而後工人i給木板t+1.....木板j進行塗色。而後,t是受到 j 和 s[i] 的約束的,必須知足j - (t + 1) + 1 <= L
爲何咱們要把轉移發成寫成那樣子呢?
想一想咱們編程的時候是怎麼安排計算順序的:
for i = 0 to K
for j = 0 to N
計算f(i,j)

當咱們在最內層循環計算f(i,j)的時候,咱們能夠認爲i是一個常數,這樣的話,當咱們把轉移方程寫成(#)那樣子的話,咱們就成功把j和t分開,這樣子,咱們就能夠在不一樣的狀態之間共享方程裏相同的一部分,並且,咱們能夠發現,t的上下限都是不減的,因而,咱們就可使用單調隊列了。

記得,寫代碼以前,在紙上,畫個圖,清清楚楚地弄明白各個量的關係,初始值,約束等等!
千萬別一上來就coding,搞清楚細節先!


看看最後一種狀況,若是j >= s[i],而且 j - s[i] + 1 > L[i],這時候的方程是很顯然的:
f(i,j) = f(i-1,j)或者 f(i-1,s[i] - 1) + P[i] * L[i]

記得排序啊編程

 

#include <deque>
#include <algorithm>
#include <utility>
#include <cstdio>
#include <cassert>
using namespace std;


typedef pair<int, int> PairII;

inline int dist(int start, int end) { return end - start + 1; }

template<class DQ>
void pushElem(DQ & q, PairII p) {
	while (!q.empty() && q.back().second <= p.second) q.pop_back();
	q.push_back(p);
}

struct Worker {
	int L, S, P;
};

bool cmpWorker(const Worker & a, const Worker & b) {
	return a.S < b.S;
}

deque<PairII> Q;
int N, K;
int f[101][16001];
Worker a[101];

void dp() {
	int i, j;

	for (i = 0; i <= K; ++i) {
		int t = 0;
		for (j = 0; j <= N; ++j) {
			int & ans = f[i][j];
			ans = 0;
			if (i == 0) {
				ans = 0;
			} else if (j == 0) {
				ans = 0;
			} else if (j < a[i].S) {
				ans = f[i - 1][j];
			} else if (a[i].S <= j && dist(a[i].S, j) < a[i].L) {
				ans = f[i - 1][j];
				int rest = a[i].L - dist(a[i].S, j);
				int left = max(0, a[i].S - rest - 1);
				while (t <= a[i].S - 1) { pushElem(Q, make_pair(t, f[i - 1][t] + a[i].P * dist(t + 1, a[i].S))); t += 1; }
				while (!Q.empty() && Q.front().first < left) Q.pop_front();
				if (!Q.empty()) { ans = max(ans, Q.front().second + dist(a[i].S + 1, j) * a[i].P); }
			} else if (dist(a[i].S, j) >= a[i].L) {
				ans = max(f[i - 1][j], f[i - 1][a[i].S - 1] + a[i].L * a[i].P);
			} else {
				assert(0);
			}
		}
	}
}

void input() {
	scanf("%d %d", &N, &K);
	int i, j;
	for (i = 1; i <= K; ++i) {
		scanf("%d %d %d", &(a[i].L), &(a[i].P), &(a[i].S));
	}

	//由於尼瑪忘記排序,wa了好屢次!!!
	sort(a + 1, a + K + 1, cmpWorker);
}

int main() {
	input();
	dp();
	printf("%d\n", f[K][N]);
	return 0;
}
相關文章
相關標籤/搜索