Fence POJ1821

\(dp[i][j]\) 表示前i个工人,其中第i个工人往他所占位置的右边涂了j格的最高工钱

\(dp[i][j] = max(dp[p][q] + min(s[i] - s[p] - q , L[i] - j) * p[i] + p[i] * j)\)

分开考虑:

1、先考虑一定能够涂满的方式

\(dp[i][j] = max(dp[p][q] + L[i] * p[i]) (s[p] + q <= s[i] - L[i] + j)\)

显然可以开桶处理

2、考虑不一定涂得满

考虑把前面的所有dp[i]压成一个桶处理

\(dp[i][j] = max(a[k] - k * p[i]) + p[i] * j + i * p[i]; (k > s[i] - L[i] + j)\)

显然可以用单调队列维护

时间复杂度O(nk) , 空间复杂度O(n)

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 2e4 + 5;
int n , k;
int a[MAXN] , m[MAXN] , dp[MAXN] , q[MAXN] , h , r;

struct node {
	int l , p , s;
}worker[105];
bool cmp(node x , node y) {
	return x.s < y.s;
}

int main() {
//	freopen("1.in" , "r" , stdin);
	scanf("%d %d" , &n , &k);
	for (int i = 1; i <= k; ++i) scanf("%d %d %d" , &worker[i].l , &worker[i].p , &worker[i].s);
	sort(worker + 1 , worker + 1 + k , cmp);
	for (int i = 1; i <= k; ++i) {
		int rl = 0;
		if(worker[i].s + worker[i].l - 1 > n) rl = n - worker[i].s;
		else rl = worker[i].l - 1; 
		for (int j = 0; j <= rl; ++j) {
			if(worker[i].s - worker[i].l + j < 0) break;
			dp[j] = m[worker[i].s - worker[i].l + j] + worker[i].l * worker[i].p;
		}
		h = 1 , r = 0;
		for (int j = max(worker[i].s - worker[i].l + 1 , 0); j < worker[i].s; ++j) {
			while(h <= r && a[q[r]] - q[r] * worker[i].p <= a[j] - j * worker[i].p) r --;
			q[++r] = j;
		}
		for (int j = 0; j <= rl; ++j) {
			while(h < r && q[h] < worker[i].s - worker[i].l + j) h ++;
			dp[j] = max(dp[j] , a[q[h]] - q[h] * worker[i].p + worker[i].p * j + worker[i].s * worker[i].p);
		}
		for (int j = 0; j <= rl; ++j) a[j + worker[i].s] = max(a[j + worker[i].s] , dp[j]);
		m[0] = a[0];
		for (int j = 1; j <= n; ++j) m[j] = max(a[j] , m[j - 1]);
	}
	printf("%d" , m[n]);
	return 0;
}
posted @ 2020-09-15 22:23  Reanap  阅读(63)  评论(0编辑  收藏  举报