bzoj3450 Tyvj1952 Easy

本文介绍了一种使用动态规划解决特定序列问题的方法,通过计算连续字符的期望长度和分数,来找出序列的最优解。文章详细解释了状态转移方程,并提供了两种不同的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


个人认为这道题和收集邮票或多或少有相似的地方。
它们都有一个特点,就是某一步产生的贡献和这一步的信息有关。
解决分两步:第一步,计算步长的期望。第二步,找到不同步长之间贡献差值的关系式。
切入点:先假设每一步的贡献都是 1 1 1
然后递推出步长的贡献后,用找到的关系式去计算真实的贡献。


l [ i ] l[i] l[i]表示以第 i i i位结尾的连续 o o o的期望长度。
f [ i ] f[i] f[i]表示到第 i i i位为止的期望分数。

那么对于 l [ i ] l[i] l[i],这一段 o o o对应的贡献就是 l [ i ] 2 l[i]^2 l[i]2。(根据题中定义)

如果已知 l [ i − 1 ] l[i-1] l[i1],假设第 i i i位就是 o o o,那么这时候有
l [ i ] = l [ i − 1 ] + 1 l[i]=l[i-1]+1 l[i]=l[i1]+1
当前的贡献就是
l [ i ] 2 = ( l [ i − 1 ] + 1 ) 2 = l [ i − 1 ] 2 + 2 × l [ i − 1 ] + 1 l[i]^2=(l[i-1]+1)^2=l[i-1]^2+2\times l[i-1]+1 l[i]2=(l[i1]+1)2=l[i1]2+2×l[i1]+1
而原来的贡献是 l [ i − 1 ] 2 l[i-1]^2 l[i1]2,发现这时增加了 2 × l [ i − 1 ] + 1 2\times l[i-1]+1 2×l[i1]+1。当然,如果这一位是 x x x贡献就是 0 0 0。如果是 ? ? ?,就用对应情况的概率*对应情况的期望就好了。这样就可以直接递推了。

现在考虑转移。
如果当前位置为 x x x,那么 l [ i ] = 0 , f [ i ] = f [ i − 1 ] l[i]=0,f[i]=f[i-1] l[i]=0,f[i]=f[i1]这很显然,因为这里的贡献为 0 0 0

如果当前位置为 o o o,那么 l [ i ] = l [ i − 1 ] + 1 , f [ i ] = f [ i − 1 ] + 2 × l [ i − 1 ] + 1 l[i]=l[i-1]+1,f[i]=f[i-1]+2\times l[i-1]+1 l[i]=l[i1]+1,f[i]=f[i1]+2×l[i1]+1根据刚才推导的式子可以得出。

如果当前位置为 ? ? ?,那么用对应情况的概率*对应情况的期望。有 x x x o o o两种可能。那么
f [ i ] = f [ i − 1 ] + ( 2 × l [ i − 1 ] + 1 ) + ( 0 ) 2 , l [ i ] = ( l [ i − 1 ] + 1 ) + ( 0 ) 2 f[i]=f[i-1]+\frac{(2\times l[i-1]+1)+(0)}{2},l[i]=\frac{(l[i-1]+1)+(0)}{2} f[i]=f[i1]+2(2×l[i1]+1)+(0),l[i]=2(l[i1]+1)+(0)

最后答案就是 f [ n ] f[n] f[n]

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
double l[maxn],f[maxn];
int n;char ch[maxn];
int main(){
	scanf("%d%s",&n,ch+1);
	for(int i=1;i<=n;++i){
		if(ch[i]=='x') f[i]=f[i-1],l[i]=0;
		if(ch[i]=='o') f[i]=f[i-1]+2.0*l[i-1]+1.0,l[i]=l[i-1]+1.0;
		if(ch[i]=='?') f[i]=f[i-1]+l[i-1]+0.5,l[i]=(l[i-1]+1.0)/2.0;
 }printf("%.4lf",f[n]); 
}

当然, ( 2 × l [ i − 1 ] + 1 ) (2\times l[i-1]+1) 2×l[i1]+1只是一个关系的形式,只要找到关系,怎么写都可以,像下面这样。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
double l[maxn],f[maxn];
int n;char ch[maxn];
int main(){
	scanf("%d%s",&n,ch+1);
	for(int i=1;i<=n;++i){
		
		if(ch[i]=='x')
		f[i]=f[i-1],l[i]=0;
		
		if(ch[i]=='o')
		l[i]=l[i-1]+1.0,f[i]=f[i-1]-l[i-1]*l[i-1]+l[i]*l[i];
		
		if(ch[i]=='?')
		f[i]=f[i-1]+((l[i-1]+1)*(l[i-1]+1)-l[i-1]*l[i-1])/2.0,
		l[i]=(l[i-1]+1.0)/2.0;

	}printf("%.4lf",f[n]); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值