[poj 1821] Fence {单调队列优化dp}

版权声明:请大家斧正,如喜欢的话,为拙见点一个赞吧。 https://blog.csdn.net/qq_39897867/article/details/84840524

题目

http://poj.org/problem?id=1821


解题思路

先把所有工匠按照 S i S_{i} 排序,这样,每个工匠粉刷的木板一定在上一个工匠粉刷的木板之后。
F [ i , j ] F[i,j] 表示安排前 i i 个工匠粉刷前 j j 块木板,工匠们能获得的最多报酬:

  1. F [ i , j ] = F [ i 1 , j ] F[i,j]=F[i-1,j]
  2. F [ i , j ] = F [ i , j 1 ] F[i,j]=F[i,j-1]
  3. F [ i , j ] = m a x { F [ i 1 , k ] + P i ( j k ) } F[i,j]=max\{ F[i-1,k]+P_{i}*(j-k) \} ,其中 j L i k S i 1 , j S i j-L_{i}\leq k \leq S_{i}-1,j \geq S_{i}

然后我们可以用单调队列维护一个决策点 k k 单调递增,数值 F [ i 1 , k ] P i k F[i-1,k]-P_{i}*k 单调递减的队列。支持如下几种操作:

  1. j j 变大时,检查队头元素,把小于 j L i j-L_{i} 的决策出队。
  2. 需要查询最有决策时,队头即为所求。
  3. 有一个新的决策要加入候选集合时,在队尾检查 F [ i 1 , k ] P i k F[i-1,k]-P_{i}*k 的单调性,把无用决策从队尾直接出队,最后把新决策加入队列。

代码

#include<cstdio>
#include<algorithm>
using namespace std; 
struct rec{int L,P,S;}a[110];
int n,m; 
int f[110][16010],q[16010]; 
bool operator <(rec a,rec b){return a.S<b.S;}
int calc(int i,int k){return f[i-1][k]-a[i].P*k;}
int main()
{
	scanf("%d%d",&n,&m); 
	for (int i=1;i<=m;i++) scanf("%d%d%d",&a[i].L,&a[i].P,&a[i].S);
	sort(a+1,a+m+1); 
	for (int i=1;i<=m;i++){
		int l=1,r=0; //初始化单调队列
		for (int k=max(0,a[i].S-a[i].L);k<=a[i].S-1;k++){
			while (l<=r&&calc(i,q[r])<=calc(i,k)) r--; //插入新决策,维护队尾单调性
			q[++r]=k; 
		}
		for (int j=1;j<=n;j++){
			f[i][j]=max(f[i-1][j],f[i][j-1]);		//不粉刷时的转移
			if (j>=a[i].S) {
				while (l<=r&&q[l]<j-a[i].L) l++; //排除队头不合法决策
				if (l<=r) f[i][j]=max(f[i][j],calc(i,q[l])+a[i].P*j); //队列非空时,取队头进行状态转移
			}
		}
	}
	printf("%d",f[m][n]); 
}

猜你喜欢

转载自blog.csdn.net/qq_39897867/article/details/84840524