粉刷木板【DP】【单调队列】

>Link

ybtoj粉刷木板


>Description

有 N 块木板从左到右排成一行,有 M 个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。

第 i 个木匠要么不粉刷,要么粉刷包含木板 s i s_i si 且长度不超过 l i l_i li 的连续的一段木板,每粉刷一块可以得到 p i p_i pi 的报酬。不同工匠的 p i p_i pi 不同。 请问如何安排能使工匠们获得的总报酬最多。

N ≤ 16000 , M ≤ 100 N\le 16000,M\le 100 N16000,M100


>解题思路

我们可以进行DP,设 f i , j f_{i,j} fi,j为前 i i i个木板,让前 j j j个木匠来刷的最大总报酬
f i , j = m a x ( f i − 1 , j , f i , j − 1 , ( i − l i ≤ k < s j ) f k , j − 1 + p j ∗ ( i − k ) ) f_{i,j}=max(f_{i-1,j},f_{i,j-1},(i-l_i \le k<s_j)f_{k,j-1}+p_j*(i-k)) fi,j=max(fi1,j,fi,j1,(ilik<sj)fk,j1+pj(ik))
情况一:第 i i i块木板不刷;情况二:第 j j j个木匠不刷;情况三:第 j j j个木匠刷区间 [ k + 1 , i ] [k+1,i] [k+1,i]的木板

情况一二很好处理,我们现在优化情况三
把式子拆开 f i , j = p j ∗ i + m a x ( f k , j − 1 − p j ∗ k ) f_{i,j}=p_j*i+max(f_{k,j-1}-p_j*k) fi,j=pji+max(fk,j1pjk)
对于后面这个包含 k k k的部分,我们发现 k k k的范围是不断后移的,所以我们可以搞一个单调队列来维护它

不知道为什么打的时候输出那里出锅了,要拿个ans记录 f i , j f_{i,j} fi,j的最大值输出才能A QwQ(然后我改了好久


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 16010
#define LL long long
using namespace std;

struct node
{
    
    
	int l, s; LL p;
} a[N];
int n, m, head, tail, pos[N], l, r;
LL f[N][110], q[N], ans;

bool cmp (node aa, node bb) {
    
    return aa.s < bb.s;}

int main()
{
    
    
	scanf ("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	  scanf ("%d%lld%d", &a[i].l, &a[i].p, &a[i].s);
	sort (a + 1, a + 1 + m, cmp);
	for (int j = 1; j <= m; j++)
	{
    
    
		head = 1, tail = 0;
		memset (q, 0, sizeof (q));
		memset (pos, 0, sizeof (pos));
		for (int i = max (0, a[j].s - a[j].l); i < a[j].s; i++) //可以选的k的范围,先把最前面的那段范围放进来(后面慢慢把左端点后移)
		                                             //因为我们发现k的范围的右端点是不会变化的,所以我们后续就不用扩大单调队列了
		{
    
    
			LL sum = f[i][j - 1] - a[j].p * (LL)i;
			while (head <= tail && q[tail] < sum) tail--;
			tail++;
			q[tail] = sum, pos[tail] = i;
		}
		for (int i = 1; i <= n; i++)
		{
    
    
			f[i][j] = max (f[i - 1][j], f[i][j - 1]);
			ans = max (ans, f[i][j]);
		}
		for (int i = a[j].s; i <= min (n, a[j].s + a[j].l - 1); i++) //i的范围
	    {
    
    
	    	int k = max (0, i - a[j].l);
	 	 	while (head <= tail && pos[head] < k) head++;
		  	if (head <= tail)
			  f[i][j] = max (f[i][j], q[head] + a[j].p * (LL)i);
			ans = max (ans, f[i][j]);
		  	//for (int k = max (0, i - a[j].l); k < a[j].s; k++)
		  	//  f[i][j] = max (f[i][j], f[k][j - 1] + a[j].p * (i - k));
		}
	}
	printf ("%lld", ans);
	return 0;
}

Guess you like

Origin blog.csdn.net/qq_43010386/article/details/121000389
DP
Recommended