专栏文章
题解:P10978 Fence
P10978题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mipvehf1
- 此快照首次捕获于
- 2025/12/03 18:34 3 个月前
- 此快照最后确认于
- 2025/12/03 18:34 3 个月前
(以下内容受到李煜东《算法竞赛进阶指南》的启发,如有侵权,联系删除。同时鸣谢前辈及更多人做出的贡献。)
前置知识:单调队列。
这道题较为显然的使用 dp。
设 表示前 个工人刷了前 块木板能获得的最大价值,且为了能够转移需要允许前 块木板有的不刷。
-
如果让第 个工人一块木板都不刷,那么 。
-
如果让第 块木板不被刷,那么 。
-
如果让第 个工人刷第 块到 第 块木板,那么 。其中 应满足 。
注意一点,为了保证枚举顺序正确,需先按照 从小到大排序。
暴力转移是 的,这道题重点就在于优化上。
枚举 和 是必须的,考虑优化掉枚举 。
先改写一下第三个转移方程,当 和 固定时,可写为:
(为方便表述,将 设为 )
不难发现,当外层循环 不变,内层循环 逐渐变大时, 的范围的下界逐渐增大,上界不变。
似乎是......单调队列?
验证发现,的确满足单调队列的性质,需要维护一个决策点 单调递增, 单调递减的队列,只有这个队列中的决策有可能成为某一时刻选择的最优决策。
然后就是经典的单调队列优化 dp。
结合代码食用:
CPP#include<bits/stdc++.h>
using namespace std;
struct nod
{
int l,p,s;
}a[16005];
int f[105][16005],q[16005];
int cmp(nod a,nod b)
{
return a.s<b.s;
}
int work(int u,int v)
{
return f[u-1][v]-a[u].p*v;
}
int main()
{
int n,k;
cin>>n>>k;
for(int i=1;i<=k;i++)
cin>>a[i].l>>a[i].p>>a[i].s;
sort(a+1,a+k+1,cmp);
for(int i=1;i<=k;i++)
{
int h=1,t=0;
for(int j=max(0,a[i].s-a[i].l);j<=a[i].s-1;j++)
{
while(h<=t&&work(i,q[t])<=work(i,j))
t--;
q[++t]=j;
}
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(h<=t&&q[h]<j-a[i].l)
h++;
if(h<=t)
f[i][j]=max(f[i][j],work(i,q[h])+a[i].p*j);
}
}
}
cout<<f[k][n];
return 0;
}
总结(与本题无关)
-
单调队列或单调栈的核心思想:及时排除永远不可能成为最优决策的决策。
-
能使用单调队列优化的 dp 方程的基本结构:上面转移方程中最大值可替换成最小值。详细解释一下: 和 是关于变量 的一次函数,可以限制 的范围并保证上下界变化的单调性。是关于 和 的多项式函数,且 其中每部分只与一个变量有关,这是能使用单调队列的基本条件,可以简单理解为当一个变量变化时,较优的决策仍会较优,不会产生乱序的现象。顺带一提, 的形式往往决定需要用什么样的优化策略。
-
基本步骤:1.当范围缩小时,从队首将不符合范围限制的决策出队。2.有新决策加入队列时,在队尾检查单调性,将无用决策从队尾出队,最后把新决策入队。3.查询最优决策时,队首即为所求。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...