POJ1821 Fence

题目大意

长度为n的墙,k个粉刷匠。第 i 个粉刷匠在 s[i] 块木板前,他最多可以刷包含 s[i] 的长度为 l[i] 的区间,他刷单位长度获得钱 p[i] 。求k个粉刷匠最多能赚多少钱?

递归式的产生

动规首先要有方向,所以把所有的粉刷匠根据s[i]排序。影响赚钱最大值的因素有选了哪几个人,以及人是怎么粉刷的。所以定义DP[i][j]为前i个粉刷匠负责到木板j时赚钱最大值。分情况:1.j没有被i刷(1)i什么木板都没刷(①)(2)i刷了一些木板,但没有刷到j(②)。2.j被i刷(③)。

定义k为粉刷匠i-1所刷到的最后一个木板的位置,则总递归式为:

DP[i][j] = max{①DP[i-1][j], ②DP[i][j-1], ③if(j>=S[i]) max foreach k(max(0, j-L[i])<=k<=S[i]-1) (DP[i-1][k] + P[i] * (j-k))}。

单调队列优化

当i固定时,我们要对每一个j,在一个已知区间[max(0, j-L[i]), S[i]-1]中求最值,区间随着j的增大在从左往右滑动,所以想到对每个i构造单调队列。队列中单调的只能有k,于是提出P[i]*j,将③变为P[i]*j+max{DP[i-1][k]+P[i]-k}。这样维护一个关于k,DP[i-1][k]+P[i]-k单调递减的单调序列即可达到优化的效果。

注意

  • k值不应当定义为粉刷匠i从第k个木板开始刷,因为这样单调队列里的值关于k-1单调,翻来倒去导致了混乱。
  • 看以下代码:
    for (int k2 = max(0, s - len); k2 <= s; k2++)
            {
                int k1 = q.front(); 
                while (!q.empty() && DP[i - 1][k1] - p*k1 <= DP[i - 1][k2] - p*k2)
                    q.pop_front();
                q.push_back(k2);
            }
    //s:s[i] p:p[i]

     这里错误非常多:

  1. 此处k1永远是循环刚开始的q.front(),此后一直都不变。
  2. k2<=s:循环轮到s-1时,s-1已被处置,不需要多处理一次s-1+1。
  3. 本循环是在处置队尾,所以是q.pop_back(),而不是q.pop_front()。

完整代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdarg>
using namespace std;

#define LOOP(i,n) for(int i=1; i<=n; i++)

void _printf_(const char * format, ...)
{
#ifdef _DEBUG
	//va_list args;
	//va_start(args, format);
	//vprintf(format, args);
	//va_end(args);
#endif
}

const int MAX_BLOCK = 17000, MAX_WORKER = 110;
int DP[MAX_WORKER][MAX_BLOCK];
int totWorker, totBlock;

struct IntDeque
{
	int a[MAX_BLOCK], head, tail;
	void clear() { head = tail = 0; }
	void push_back(int x) { a[tail++] = x; }
	void pop_back() { tail--; }
	void pop_front() { head++; }
	bool empty() { return head == tail; }
	int front() { return a[head]; }
	int back() { return a[tail-1]; }
	void Tranvas() { for (int i = head; i < tail; i++)_printf_("%d ", a[i]); _printf_("\n"); }
};

struct Worker
{
	int Start, Price, Len;
	bool operator <(const Worker a)const
	{
		return Start < a.Start;
	}
}_workers[MAX_WORKER];

int Dp()
{//i:worker j:block
	memset(DP, 0, sizeof(DP));
	static IntDeque q;
	LOOP(i, totWorker)
	{
		q.clear();
		int p = _workers[i].Price, s = _workers[i].Start, len = _workers[i].Len;
		for (int k2 = max(0, s - len); k2 <= s -1; k2++)
		{
			int k1 = 0; 
			while (!q.empty() && DP[i - 1][k1=q.back()] - p*k1 <= DP[i - 1][k2] - p*k2)
				q.pop_back();
			q.push_back(k2);
		}
		LOOP(j, totBlock)
		{
			DP[i][j] = max(DP[i - 1][j], DP[i][j - 1]);
			//q.Tranvas();
			_printf_("DP[%d][%d]=%d\n", i, j, DP[i][j]);
			if (j >= s)
			{
				int len = j - _workers[i].Len;
				while (!q.empty() && q.front() < len)
					q.pop_front();
				int k = q.front();
				if (!q.empty())
					DP[i][j] = max(DP[i][j], DP[i - 1][k] + p*(j - k));
				//q.Tranvas();
				_printf_("DP[%d][%d]=%d\n", i, j, DP[i][j]);
			}
		}
	}
	return DP[totWorker][totBlock];
}

int main()
{
#ifdef _DEBUG
	freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
	scanf("%d%d", &totBlock, &totWorker);
	LOOP(i, totWorker)
		scanf("%d%d%d", &_workers[i].Len, &_workers[i].Price, &_workers[i].Start);
	sort(_workers + 1, _workers + totWorker + 1);
	printf("%d\n", Dp());
	return 0;
}

  

posted @ 2018-03-01 22:54  headboy2002  阅读(62)  评论(0编辑  收藏  举报