ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

小清新简单dp题(二)

2022-07-16 16:00:23  阅读:153  来源: 互联网

标签:int 我们 队列 dp 简单 区间 清新 单调


9. P1453 城市环路

很容易发现这其实就是一道基环树上的 dp 题。

如果是在普通树上该怎么做呢?考虑设 \(dp_{i,0/1}\) 表示以 \(i\) 为根的子树内,\(i\) 节点不选/选时贡献的最大值,有转移方程:

\[dp_{i,0}=\sum_{j\in son_i} \max(dp_{j,0},dp_{j,1}) \]

\[dp_{i,1}=\sum_{j\in son_i} dp_{j,0} \]

基环树相比普通树其实就只是多了一条边而已,而一条边的意义就在于相比于普通树,该边两端的节点也不能被同时选择。

于是我们可以利用并查集,找出第一对在加边过程中在并查集同一集合中的节点 \(S,T\),可以确定它们就是基环上的两个相邻节点。

然后我们可以以 \(S\) 和 \(T\) 分别作为根节点做两边 dp,将 \(dp_{S,0}\) 和 \(dp_{T,0}\) 取最大值即为最终答案。由于两个值无论哪个都表示不选其中的一个节点,所以可以证明在最终的方案中 \(S\) 和 \(T\) 一定不会同时被选,即可完成此题。


10. P3594 [POI2015]WIL-Wilcze doły

双指针+单调队列。

贪心地想,由于序列中的数都是正整数,我们在选中区间覆盖 \(d\) 个长度一定比覆盖 \(d-1\) 个长度优。即使我们只选了 \(d-1\) 个数,多选一个一定还是更优的(长度变长了)。所以我们可以将覆盖长度默认为 \(d\)。而默认之后,可以供我们选择覆盖、长度为 \(d\) 的区间只剩下了 \(n-d+1\) 个,且从左到右均匀分布。

题意让我们先覆盖一段为 \(0\) 在选区间,这样太麻烦,我们不要。

于是我们反过来想。如果我们先选区间,再选择其中的一段覆盖,那么由于覆盖长度确定,我们覆盖的一定是该区间之中区间和最大的一段长度为 \(d\) 的区间。所以我们可以考虑先选择区间。

考虑找到每一个 \(r\),随着 \(r\) 的值不断递增,\(l\) 一定也是单调递增的。又因为我们选择的覆盖区间一定是所选区间的一部分,所以覆盖区间大体上也是单调递增的。

于是我们突然想到了单调队列。

考虑从 \(d\) 开始枚举每一个 \(r\),并将该 \(r\) 产生的新的长度为 \(d\) 的区间 \((r-d+1,r)\) 按照正常程序放入单调队列(剔除队尾区间和小于该区间区间和的区间(莫名绕口))。注意放入单调队列时,由于我们可以通过 \(r\) 来推出区间和,而不能通过区间和推出 \(r\),所以只需要放入 \(r\) 即可代表整个区间。

然后我们再来审视目前的区间,如果此时的区间和减去最大的覆盖区间和仍然大于 \(p\)(覆盖区间和即为单调队列队首区间的区间和),我们就可以将 \(l\) 加 \(1\),并剔除单调队列队首在所选区间范围之外的覆盖区间。

每次枚举 \(r\) 之后将 \(ans\) 和目前区间的答案取最大值即可。

for (int l=1,r=d;r<=n;r++){
	while (head<=tail&&len(deq[tail])<len(r)) --tail;
	deq[++tail]=r;
	while (sum[r]-sum[l-1]-len(deq[head])>p){
		++l;
		if (head<=tail&&deq[head]<l+d-1) ++head;
	}
	ans=max(ans,r-l+1);
}

11. P3572 [POI2014]PTA-Little Bird

首先很容易想到,设 \(dp_i\) 为到第 \(i\) 个树上时的最小代价,则状态转移方程为:

\[dp_{i}=\min(dp_j+[d_j\le d_i]\ | \ i-k\le j<i) \]

但是由于数据范围要求我们在 \(O(n)\) 的时间内做完本题,而如果通过枚举前面的 \(dp\) 数组转移当前 \(dp\) 项的复杂度却是 \(O(n^2)\) 的,必定超时,所以我们不得不思考优化方法。

考虑如果现在有两个合法决策 \(x\) 和 \(y\) 可以供 \(dp_i\) 选择,其中 \(x<y\),那么贪心地来看,如果满足下面两个条件之一:

  • \(dp_x>dp_y\)

  • \(dp_x=dp_y\) 且 \(d_x<d_y\)

那么决策 \(x\) 就可以抛弃掉了。否则,\(x\) 也不会比 \(y\) 更劣。

看到这个想到了什么?对于一个决策,既可以由于离开可供选择的范围而被剔除,也可以被后来的更优决策剔除……

单调队列!!!!

我们可以对于所有决策动态建立一个单调队列!

考虑循环枚举每一个 \(dp_i\)。首先由于开始就在第一个树上,所以 \(dp_1=0\),然后将 \(1\) 塞入单调队列。对于 \(dp_2\) 到 \(dp_n\),我们只需要每次选择单调队列队首决策即为当前的最优决策,直接转移即可。然后根据决策筛选条件(上面两个)按照正常程序将当前 \(dp\) 项放入队列,最后 \(dp_n\) 即为答案。

dq[++tail]=1;
for (int i=2;i<=n;i++){
	dp[i]=dp[dq[head]];
	if (d[dq[head]]<=d[i]) dp[i]++;
	while (head<=tail&&(dp[dq[tail]]>dp[i]||dp[dq[tail]]==dp[i]&&d[dq[tail]]<d[i])) --tail;
	dq[++tail]=i;
	if (head<=tail&&dq[head]<=i-k) ++head;
}
printf("%d\n",dp[n]);

12. P3287 [SCOI2014]方伯伯的玉米田

首先要证明一个性质:每次拔高区间的右端点是 \(n\) 一定不劣。

怎么证明呢?我们不妨感性地思考一下。假设我们选择了区间 \([x,n-1]\),那么

  • 若原本 \(h_{n-1}=h_n\),则现在 \(h_{n-1}>h_n\),答案由此可能被我们搞得更劣。

  • 若原本 \(h_{n-1}>h_n\) 或 \(h_{n-1}<h_n\),则无论是选择 \([x,n-1]\) 还是 \([x,n]\) 对答案都没有任何影响,反而后者可以更好的保持 \(h_n\) 的领先优势或者维持落后距离。

综上所述,我们可以默认每次拔高区间的右端点是 \(n\) 。

那么我们可以设 \(dp_{i,j}\) 表示以 \(i\) 结尾且拔高过 \(j\) 次时的最长不下降子序列的长度。则状态转移方程显然为:

\[dp_{i,j}=\max(dp_{x,y})+1\ |\ x\le i,y\le j,h_x+y\le h_i+j \]

方程我们推出来了,可以如果使用朴素算法四层枚举,复杂度将会达到恐怖的 \(O(n^2k^2)\),显然舍去。

所以我们考虑使用数据结构动态维护 \(dp_{x,y}\) 的最大值,选用二维线段树即可。由于没有修改操作,我们这里选用了常数较小的二维树状数组。

考虑两层循环分别正序枚举 \(i\) 和倒序枚举 \(j\)(类似背包问题原理),并在每次枚举后先查询前面 \(dp_{x,y}\) 的最大值,用它更新答案后,再将最大值放入树状数组,持续维护即可。

最终输出的答案即为最终答案。

int lowbit(int x){ return x&(-x); }
int query(int nh,int nk){
	int ans=0;
	for (int i=nh;i;i-=lowbit(i))
		for (int j=nk;j;j-=lowbit(j))
			ans=max(ans,c[i][j]);
	return ans;
}
void updata(int nh,int nk,int val){
	for (int i=nh;i<=ma+k;i+=lowbit(i))
		for (int j=nk;j<=k+1;j+=lowbit(j))
			c[i][j]=max(c[i][j],val);
}
int main(){
	n=read(),k=read();
	for (int i=1;i<=n;i++) a[i]=read(),ma=max(ma,a[i]);
	for (int i=1;i<=n;i++)
		for (int j=k;j>=0;j--){
			int x=query(a[i]+j,j+1)+1;
			ans=max(ans,x);
			updata(a[i]+j,j+1,x);
		}
	printf("%d\n",ans);
	return 0;
}

13. P2900 [USACO08MAR]Land Acquisition G

首先如果我们将每一个土地看成一块矩形,那么对于一个土地,如果存在另一个土地长宽都大于等于它,那么前一个土地对答案一定是没有任何贡献的,因为我们可以在购买后一个土地的时候顺便就将前一个土地买了。所以我们可以在输入之后就将所有矩形排序,最终使得所有土地的长度单调递增,宽度单调递减,将没有用的土地剔除出去、

考虑朴素做法,对于 \(dp_i\),朴素做法的状态转移方程显然为:

\[dp_{i}=\min(dp_i,dp_j+w_{j+1}h_i) \]

枚举 \(j\) 则复杂度达到 \(n^2\),显然不行,那么我们考虑决策。

对于两个决策 \(j\) 和 \(k\),如果 \(j\) 优于\(k\),那么则

\[dp_j+w_{j+1}h_i\le dp_k+w_{k+1}h_i \]

移项得

\[dp_j-dp_k\le(w_{k+1}-w_{j+1})h_i \]

所以

\[h_i\ge\frac{dp_j-dp_k}{w_{k+1}-w_{j+1}} \]

于是我们用单调队列的方式维护最优决策即可。

单调队列维护时考虑两点:

  1. 维护队首。由于上面的 \(h_i\) 是单调递增的,所以即使刚开始的时候 \(h_i\) 小于右侧式子,但一旦当 \(h_i\) 大于等于右侧式子时,便永远大于了。

由此我们可以定义一个名词——临界点——当过了这个点之后,设 \(j<k\),决策 \(j\) 的贡献就永远劣于 \(k\) 了。此时我们便可以将 \(j\) 删除。

表现在单调队列上就是,当队首决策劣于队列第二个决策时,我们便可以删除队首决策,因为它将永远没有用了。(可以看做越往后的决策越偏发展型)

而表现在这道题上则是,如果队首决策和队列第二个决策满足上述式子,则可以将队首决策删去。

维护队首后便可以去队首最优决策与 \(dp_i\) 进行 \(dp\)。

  1. 维护队尾。考虑三个决策 \(i\),\(j\),\(k\)。

如果 \(i\) 和 \(j\) 的临界点大于 \(j\) 和 \(k\) 的临界点,那么当 \(j\) 优于 \(i\)、将 \(i\) 剔除时,\(k\) 早已经优于 \(j\) 了。所以我们便可以在对 \(dp_i\) 转移过后再维护一下队尾,将没用的 \(j\) 剔除后,将当前决策 \(k\)(即 \(i\))放入队尾即可。

最后在 \(dp_n\) 转移过后输出答案即可。

double slope(int j,int k){
	return 1.0*(dp[j]-dp[k])/(ear[k+1].x-ear[j+1].x);
}

dq[++tail]=0;
for (int i=1;i<=n;i++){
	while (head<tail&&slope(dq[head],dq[head+1])<=ear[i].y) ++head;
	dp[i]=dp[dq[head]]+ear[dq[head]+1].x*ear[i].y;
	while (head<tail&&slope(dq[tail-1],dq[tail])>=slope(dq[tail],i)) --tail;
	dq[++tail]=i;
}

14. P1654 OSU!

考虑目前该进行第 \(i\) 次操作,前面已经连击了 \(x\) 次,那么若此次成功,则分数贡献为 \((x+1)^3-x^3=3x^2+3x+1\).

故设 \(E(f)_ i\) 为以 \(i\) 为结尾的总得分,则其显然为以 \(i-1\) 结尾的得分和第 \(i\) 次操作可能的得分之和。

设 \(E(x)_ i\) 为以 \(i\) 为结尾的连击长度 \(x\) 的期望 ,则

\[E(x)_ i=p_i\times(E(x)_ {i-1}+1)+(1-p_i)\times0=p_i(E(x)_ {i-1}+1) \]

升一维,考虑 \(x^2\) 的期望大小,则同理:

\[E(x^2)_ i=p_ i(E(x^2)_ {i-1}+2E(x)_ {i-1}+1) \]

综上可推出第 \(i\) 次操作可能的得分,得出转移方程:

\[E(f)_ i=E(f)_ {i-1}+p_i(3E(x^2)_{i-1}+3E(x)_{i-1}+1) \]

for (int i=1;i<=n;i++){
	scanf("%lf",&p);
	p1[i]=(p1[i-1]+1)*p;
	p2[i]=(p2[i-1]+2*p1[i-1]+1)*p;
	ans[i]=ans[i-1]+(3*p2[i-1]+3*p1[i-1]+1)*p;
}

15. P2135 方块消除

首先很容易想到用区间 dp,用 \(dp_{l,r}\) 表示区间 \([l,r]\) 消掉后分数的最大值。但很显然,这个设置并不能概括掉所有的状态,我们无法用这个状态进行有效转移。

为什么不能转移呢?仔细思索,我们发现这个设置无法表示例如 10101 这类方块。正解应该是先依次消去两个 0,再连续消去三个 1,但是显然我们无法用 \(dp_{l,r}\) 表示这个“连续消去”。

再深入思考,我们有时并不想一次性的消掉某个区间,而是想让这个区间的右端向左放,延迟消去。而我们要设的状态需要能够表示这个“延迟”的行为。

于是考虑设 \(dp_{l,r,p}\) 表示消去区间 \([l,r]\),且 \(r\) 右侧连着 \(p\) 个跟 \(r\) 同色的方块,消去之后贡献的最大值。

好奇怪的状态/fad

那么怎么转移呢?首先肯定需要采用区间 dp 的套路,枚举 \(k\),将 \([l,r]\) 拆成 \([l,k]\) 和 \([k+1,r]\) 分别转移。

毫无疑问,\([l,r]\) 的贡献可以是两个子区间的贡献之和;而如果 \(r\) 与 \(k\) 的颜色相同,则可以转化为区间 \([k+1,r-1]\) 的贡献,以及区间 \([l,k]\) 且右侧接上若干同色方块的贡献之和。

具体来讲,转移方程为:

\[dp_{i,j,p}=\max(dp_{i,k,0}+dp_{k+1,r,p},dp_{i,k,p+g_r}+dp_{k+1,r-1,0}),i\le k<r \]

而由于该转移方程要用到的状态比较稀疏,许多状态都不会被用到,所以可以考虑采用记忆化搜索的方式进行转移,可以优化掉非常大的常数。

int solve(int l,int r,int ex){
	if (l>r) return 0;
	if (dp[l][r][ex]) return dp[l][r][ex];
	if (l==r) return dp[l][r][ex]=(g[l]+ex)*(g[l]+ex);
	int ans=0;
	for (int k=l;k<r;k++){
		ans=max(ans,solve(l,k,0)+solve(k+1,r,ex));
		if (col[k]==col[r]) ans=max(ans,solve(k+1,r-1,0)+solve(l,k,ex+g[r]));
	}
	return dp[l][r][ex]=ans;
}

16. P2507 [SCOI2008]配对

有一个很明显的贪心是,尽量配对与自己排名相近的整数。

例如如果去掉不允许相同整数配对的条件,则将两个数组都升序或降序排序后,相对应的项配对,答案一定最优。

那么加上这个条件怎么做呢?

由于这个条件的干扰,相对应的项并不一定可以配对。但索性规定了两个数组内部元素互不相同,故对于一个整数,最多只有一个整数与其相同。

那么如果让每个整数与和自己排名相差±1的整数配对是否可行呢?显然不行。考虑对于下面数组 \(A\{1,2,3\},B\{1,2,3\}\),显然无法通过±1的干扰进行配对。

那么再退一步贪心,±2的排名是否可行呢?可以证明,是可以的。

那么如果我们将两个数组排序后,将每个整数与和其排名相差不超过 \(2\) 的另一个数组中的整数连边,是完全可以跑费用流的。不过很慢就是了

考虑 dp,设 \(dp_{i}\) 表示两个数组前 \(i\) 个整数互相匹配后的最小差值之和,对于前两项则有如下转移方程:

\[dp_{1}=|a_1-b_1| \]

\[dp_2=\min(|a_1-b_1|+|a_2-b_2|,|a_1-b_2|+|a_2-b_1|) \]

从第三项开始,第 \(i\) 项可以由前三项交换匹配,也可以前两项交换匹配,或直接匹配第 \(i\) 项。注意转移时若两项绝对值为 \(0\) ,则直接赋为极大值。

dp[1]=calc(a[1],b[1]);
dp[2]=min(calc(a[1],b[2])+calc(a[2],b[1]),calc(a[1],b[1])+calc(a[2],b[2]));
for (int i=3;i<=n;i++){
	dp[i]=min(min(dp[i-2]+calc(a[i-1],b[i])+calc(b[i-1],a[i]),dp[i-1]+calc(a[i],b[i])),
			min(dp[i-3]+calc(a[i-2],b[i])+calc(a[i-1],b[i-1])+calc(a[i],b[i-2]),
			min(dp[i-3]+calc(a[i-2],b[i-1])+calc(a[i-1],b[i])+calc(a[i],b[i-2]),
				dp[i-3]+calc(a[i-2],b[i])+calc(a[i-1],b[i-2])+calc(a[i],b[i-1]))));
}
printf("%lld\n",dp[n]);

标签:int,我们,队列,dp,简单,区间,清新,单调
来源: https://www.cnblogs.com/ydtz/p/16484428.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有