专栏文章

题解:P10978 Fence

P10978题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mipvehf1
此快照首次捕获于
2025/12/03 18:34
3 个月前
此快照最后确认于
2025/12/03 18:34
3 个月前
查看原文
(以下内容受到李煜东《算法竞赛进阶指南》的启发,如有侵权,联系删除。同时鸣谢前辈及更多人做出的贡献。)
前置知识:单调队列
这道题较为显然的使用 dp。
fi,jf_{i,j} 表示前 ii 个工人刷了前 jj 块木板能获得的最大价值,且为了能够转移需要允许前 jj 块木板有的不刷。
  • 如果让第 ii 个工人一块木板都不刷,那么 fi,j=fi1,jf_{i,j}=f_{i-1,j}
  • 如果让第 jj 块木板不被刷,那么 fi,j=fi,j1f_{i,j}=f_{i,j-1}
  • 如果让第 ii 个工人刷第 k+1k+1 块到 第 jj 块木板,那么 fi,j=maxjliksi1{fi1,k+pi×(jk)}\begin{aligned}f_{i,j}= \max_{j-l_i \le k \le s_i-1} \{{f_{i-1,k}+p_i \times(j-k)}\}\end{aligned}
    其中 jj 应满足 jsij\ge s_i
注意一点,为了保证枚举顺序正确,需先按照 sis_i 从小到大排序。
暴力转移是 O(n2k)O(n^2k) 的,这道题重点就在于优化上。
枚举 iijj 是必须的,考虑优化掉枚举 kk
先改写一下第三个转移方程,当 iijj 固定时,可写为:
fi,j=pi×j+maxjliksi1{fi1,k+pi×k}\begin{aligned}f_{i,j}= p_i\times j+\max_{j-l_i \le k \le s_i-1} \{f_{i-1,k}+p_i \times k\}\end{aligned}
(为方便表述,将 fi1,k+pi×k{f_{i-1,k}+p_i \times k} 设为 g(i,k)g(i,k)
不难发现,当外层循环 ii 不变,内层循环 jj 逐渐变大时,kk 的范围的下界逐渐增大,上界不变。
似乎是......单调队列?
验证发现,的确满足单调队列的性质,需要维护一个决策点 kk 单调递增,g(i,k)g(i,k) 单调递减的队列,只有这个队列中的决策有可能成为某一时刻选择的最优决策。
然后就是经典的单调队列优化 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 方程的基本结构:
    fi=maxL(i)jR(i){fj+val(i,j)}\begin{aligned} f_i= \max_{L(i)\le j \le R(i)} \{f_j+val(i,j)\}\end{aligned}
    上面转移方程中最大值可替换成最小值。
    详细解释一下:L(i)L(i)R(i)R(i) 是关于变量 ii 的一次函数,可以限制 jj 的范围并保证上下界变化的单调性。
    val(i,j)val(i,j) 是关于 iijj 的多项式函数,且 其中每部分只与一个变量有关,这是能使用单调队列的基本条件,可以简单理解为当一个变量变化时,较优的决策仍会较优,不会产生乱序的现象。
    顺带一提,val(i,j)val(i,j) 的形式往往决定需要用什么样的优化策略。
  • 基本步骤:
    1.当范围缩小时,从队首将不符合范围限制的决策出队。
    2.有新决策加入队列时,在队尾检查单调性,将无用决策从队尾出队,最后把新决策入队。
    3.查询最优决策时,队首即为所求。

评论

0 条评论,欢迎与作者交流。

正在加载评论...