博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
poj 1821 - dp,单调队列
阅读量:5892 次
发布时间:2019-06-19

本文共 2774 字,大约阅读时间需要 9 分钟。

hot3.png

终于做出这题了!泪流满面啊。
首先,要理解清楚题目意思。有一道线性篱笆由N个连续的木板组成。你手上有K个工人,你要叫他们给木板涂色。每个工人有3个字段:L 表示 这个工人可以涂的最大木板数目,S表示这个工人站在哪一块木板,P表示这个工人每涂一个木板可以得到的钱。要注意的是,工人i可以选择不涂任何木板,否则,他的涂色区域必须是连续的一段,并且S[i]必须包含在内。 最后还有,每块木板只能被涂一次。
理解完题目意思后,很容易联想到邮局模型。所以看看能不能用类似的方程解决。
设f(i,j)表示使用前i个工人来对前j个木板进行涂色。
由这题,学到最重要 经验就是,一定要把every细节都想清楚才开始code,不然自找麻烦。
先看看我们要计算哪些状态,也就是每一维的值域。对于i,范围应该是[0...K],对于每个i,j的范围应该是[0....N]
马上可以看到边界条件是 当i == 0或者 j==0的时候,f(i,j)=0
然后现在假设 i 和 j 都不为0。
如果j < s[i] , 显然,这时候f(i,j) = f(i - 1,j)
如果j >= s[i],并且 j - s[i] + 1 < L[i]。这种情况,表示的是,工人先可以从木板s[i]开始向右连续 j - s[i] + 1涂个木板,然后工人可以决定向左涂0...L[i] - (j -s[i] +1)个木板,所以,这个时候的转移方程为:
(#) f(i,j) = f(i-1,j) 或者 min { f(i-1,t) + P[i] * (s[i] - (t + 1) + 1) } + P[i] *(j -(s[i] + 1) + 1)
对于某个元组(i,j,t),这个方程的含义是,先由前i-1个工人给前t个木板涂色,然后工人i给木板t+1.....木板j进行涂色。然后,t是受到 j 和 s[i] 的约束的,必须满足j - (t + 1) + 1 <= L
为什么我们要把转移发成写成那样子呢?
想想我们编程的时候是怎么安排计算顺序的:
for i = 0 to K
for j = 0 to N
计算f(i,j)
当我们在最内层循环计算f(i,j)的时候,我们可以认为i是一个常数,这样的话,当我们把转移方程写成(#)那样子的话,我们就成功把j和t分开,这样子,我们就可以在不同的状态之间共享方程里相同的一部分,而且,我们可以发现,t的上下限都是不减的,于是,我们就可以使用单调队列了。
记得,写代码之前,在纸上,画个图,清清楚楚地弄明白各个量的关系,初始值,约束等等!
千万别一上来就coding,搞清楚细节先!
看看最后一种情况,如果j >= s[i],并且 j - s[i] + 1 > L[i],这时候的方程是很显然的:
f(i,j) = f(i-1,j)或者 f(i-1,s[i] - 1) + P[i] * L[i]

记得排序啊

 

#include 
#include
#include
#include
#include
using namespace std;typedef pair
PairII;inline int dist(int start, int end) { return end - start + 1; }template
void pushElem(DQ & q, PairII p) { while (!q.empty() && q.back().second <= p.second) q.pop_back(); q.push_back(p);}struct Worker { int L, S, P;};bool cmpWorker(const Worker & a, const Worker & b) { return a.S < b.S;}deque
Q;int N, K;int f[101][16001];Worker a[101];void dp() { int i, j; for (i = 0; i <= K; ++i) { int t = 0; for (j = 0; j <= N; ++j) { int & ans = f[i][j]; ans = 0; if (i == 0) { ans = 0; } else if (j == 0) { ans = 0; } else if (j < a[i].S) { ans = f[i - 1][j]; } else if (a[i].S <= j && dist(a[i].S, j) < a[i].L) { ans = f[i - 1][j]; int rest = a[i].L - dist(a[i].S, j); int left = max(0, a[i].S - rest - 1); while (t <= a[i].S - 1) { pushElem(Q, make_pair(t, f[i - 1][t] + a[i].P * dist(t + 1, a[i].S))); t += 1; } while (!Q.empty() && Q.front().first < left) Q.pop_front(); if (!Q.empty()) { ans = max(ans, Q.front().second + dist(a[i].S + 1, j) * a[i].P); } } else if (dist(a[i].S, j) >= a[i].L) { ans = max(f[i - 1][j], f[i - 1][a[i].S - 1] + a[i].L * a[i].P); } else { assert(0); } } }}void input() { scanf("%d %d", &N, &K); int i, j; for (i = 1; i <= K; ++i) { scanf("%d %d %d", &(a[i].L), &(a[i].P), &(a[i].S)); } //因为尼玛忘记排序,wa了好多次!!! sort(a + 1, a + K + 1, cmpWorker);}int main() { input(); dp(); printf("%d\n", f[K][N]); return 0;}

转载于:https://my.oschina.net/mustang/blog/60680

你可能感兴趣的文章
随意一条查询sql转换为查询结果集相应的数目
查看>>
存储控制器使用【转】
查看>>
Android 使用SwipeBackLayout实现滑动返回上一级页面——实战来袭
查看>>
环信EaseUI集成错误 Unknown type name 'NSString' NSLocalizedString
查看>>
C#读写txt文件的两种方法介绍
查看>>
指针总结与地址
查看>>
python-迭代器与生成器的区别
查看>>
Windows界面编程第四篇 异形窗体 高富帅版 ---博客
查看>>
移动终端基带芯片基本架构
查看>>
PullToRefreshScrollView嵌套SwipeMenuListView冲突问题解决
查看>>
Android 虚拟现实(virtual reality)入门指南
查看>>
颜色空间转换公式
查看>>
ControlTemplate in WPF —— Menu
查看>>
object detection[YOLO]
查看>>
自然语言处理真实项目实战(20170822)
查看>>
【FFmpeg】FFmpeg常用基本命令
查看>>
Java Software Engineer Skill Map
查看>>
匹配一组字符
查看>>
windows下命令行模式中cd命令无效的原因
查看>>
容器化操作系统概览 - DockOne.io
查看>>