版权声明:请大家斧正,如喜欢的话,为拙见点一个赞吧。 https://blog.csdn.net/qq_39897867/article/details/84840524
题目
http://poj.org/problem?id=1821
解题思路
先把所有工匠按照
排序,这样,每个工匠粉刷的木板一定在上一个工匠粉刷的木板之后。
设
表示安排前
个工匠粉刷前
块木板,工匠们能获得的最多报酬:
- ,其中
然后我们可以用单调队列维护一个决策点 单调递增,数值 单调递减的队列。支持如下几种操作:
- 当 变大时,检查队头元素,把小于 的决策出队。
- 需要查询最有决策时,队头即为所求。
- 有一个新的决策要加入候选集合时,在队尾检查 的单调性,把无用决策从队尾直接出队,最后把新决策加入队列。
代码
#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]);
}