#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MID(x,y) ((x+y)>>1)
using namespace std;
typedef long long LL;
struct worker{
int l, p, s;
}w[105];
bool cmp(worker a, worker b){
return a.s < b.s;
}
int f[105][16005];
int n,k;
void debug(){
for (int i = 0; i <= k; i ++){
for (int j = 0; j <= n; j ++)
printf("%d %d %d\n", i, j, f[i][j]);
}
}
deque Q;
void initQ(){
while(!Q.empty())
Q.pop_back();
}
int main(){
scanf("%d %d", &n, &k);
for (int i = 1; i <= k; i ++)
scanf("%d %d %d", &w[i].l, &w[i].p, &w[i].s);
sort(w+1, w+k+1, cmp); //别忘了先按S排序!
for (int i = 1; i <= k; i ++){
for (int j = 0; j < w[i].s; j ++)
f[i][j] = max(f[i-1][j], f[i][j-1]);
//单调队列
initQ();
for (int j = max(0, w[i].s - w[i].l); j < w[i].s; j ++){
while(!Q.empty() && f[i-1][Q.back()] - Q.back()*w[i].p <= f[i-1][j] - j*w[i].p)
Q.pop_back();
Q.push_back(j);
}
for (int j = w[i].s; j < min(w[i].s + w[i].l, n+1); j ++){
while (!Q.empty() && Q.front() < j - w[i].l)
Q.pop_front();
f[i][j] = max(f[i-1][j], f[i][j-1]);
f[i][j] = max(f[i][j], f[i-1][Q.front()] - Q.front()*w[i].p + j * w[i].p);
// 朴素DP:
// for (int p = max(0, j - w[i].l); p <= j; p ++)
// f[i][j] = max(f[i][j], f[i-1][p] + (j - p)*w[i].p);
}
for (int j = w[i].s + w[i].l; j <=n; j ++)
f[i][j] = max(f[i-1][j], f[i][j-1]);
}
debug();
int res = 0;
for (int j = 1; j <= n; j ++)
res = max(res, f[k][j]);
printf("%d\n", res);
return 0;
}
题目大意:有一道线性篱笆由N个连续的木板组成。有K个工人,你要叫他们给木板涂色。每个工人有3个参数:L 表示 这个工人可以涂的最大木板数目,S表示这个工人站在哪一块木板,P表示这个工人每涂一个木板可以得到的钱。要注意的是,工人i可以选择不涂任何木板,否则,他的涂色区域必须是连续的一段,并且S[i]必须包含在内。 最后还有,每块木板只能被涂一次。 状态方程比较容易想:dp[i][j]表示第i个人刷的最后一面墙是j时的最大获利,则 dp[i][j]=max(dp[i-1][k]+(j-k)*p[i]) j-l[i]+1<=k+1<=s[i] (*) dp[i][j]=max(dp[i-1][j],dp[i][j-1],dp[i][j])//第i个人不刷,第j面墙不刷 但是O(k*n^2)的复杂度显然不能接受。而针对(*)这种“特殊”的转移方程,我们可以用单调队列把它优化到O(1): dp[i][j]=max(dp[i-1][k]-k*p[i])+j*p[i] 其中j*p[i]在i,j两重循环中相当于常数,所以,对于状态dp[i][j]只要单调队列维护dp[i-1][k]-k*p[i]的最大值即可
单调队列维护过程(可以回过头看看POJ 2823---单调队列的模型): 单调队列具体的做法是:最外层循环为i,首先把j=1~s[i]-1转移完(因为它不涉及第三个转移),然后把(j-l[i]<=k<=s[i]-1)的决策点的F[i-1,k]-p[i]*k依次入队建立“入队早晚时间递增,F[i-1,k]-p[i]*k的值递减”的单调队列,接下来循环j=s[i]~s[i]+l[i]-1,进行这三个转移(第三个转移只需要用队首元素),其中每次需要把队首超出长度限制的决策点出队;最后把剩下的到n循环完,只需要前两个转移。
转载于:https://www.cnblogs.com/AbandonZHANG/archive/2013/02/21/4114223.html