围栏

有 N 块木板从左到右排成一行,有 M 个工匠对这些木板进行粉刷,每块木板至多被粉刷一次。
第 i 个木匠要么不粉刷,要么粉刷包含木板 S_i 的,长度不超过 L_i 的连续的一段木板,每粉刷一块可以得到 P_i 的报酬。
不同工匠的 S_i 不同。
请问如何安排能使工匠们获得的总报酬最多

注意: 在用单调队列的时候 优化部分只能包含决策变量,而不能包含状态变量
因为状态变量作为上下界在变化,我们能优化的前提是内部变量与边界的取值无关
因此要写成

\[f(i,j)=P_i \times j+ \max_{j-L_i\le k \le S_i-1}\{f(i-1,k)-P_i\times k\} \]

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
const int N=16010;
const int M=110;
using namespace std;
int read()
{
	int x=0,f=0,c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return f?-x:x;
}

struct Node{int L,P,S;}p[M];
bool cmp(Node x,Node y){ return x.S<y.S;}
deque<int> q;
int f[M][N],n,m; 

int calc(int i,int k){return f[i-1][k]-p[i].P*k;}

int main()
{
	n=read(); m=read();
	for(int i=1;i<=m;i++) p[i].L=read(),p[i].P=read(),p[i].S=read();
	sort(p+1,p+m+1,cmp);
	for(int i=1;i<=m;i++)//枚举人 
	{
		//先把集合插入到队列中 
		while(q.size()) q.pop_back();
		for(int k=max(p[i].S-p[i].L,0);k<=p[i].S-1;k++)
		{
			while(q.size()&&calc(i,q.back())<=calc(i,k)) q.pop_back();
			q.push_back(k);
		}
	
		for(int j=1;j<=n;j++)//枚举板子 
		{
			f[i][j]=max(f[i-1][j],f[i][j-1]);
			if(j>=p[i].S&&j<=p[i].S+p[i].L-1) 
			{
				while(q.size()&&q.front()<j-p[i].L) q.pop_front();
				f[i][j]=max(f[i][j],p[i].P*j+calc(i,q.front()));
			}
		}
	}
	printf("%d",f[m][n]);
	return 0;
}

注意;
对每个转移方程分析一个上下界的问题

posted @ 2022-02-17 21:37  __iostream  阅读(32)  评论(0)    收藏  举报