2020-11-03 NOIP模拟 T2

题解

对于 n n n较小的情况进行动态规划。
f [ i ] [ j ] f[i][j] f[i][j]为当前有i张重掷符卡,当前骰子点数为j,可能的最大期望伤害。如果使用了重掷符卡,那么从 1 + 1 6 ∑ k = 1 6 f [ i − 1 ] [ k ] 1+\frac {1}{6}\sum^{6}_{k=1}f[i-1][k] 1+61k=16f[i1][k]转移,否则丢弃这张符卡从 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j]转移。
打表可以发现对于充分大的 i i i f [ i ] [ j ] = f [ i − 1 ] [ j ] + 1 f[i][j]=f[i-1][j]+1 f[i][j]=f[i1][j]+1。即有很多张重掷符卡的时候一定会尽量用这些重掷符卡直到张数减小到一定程度,因此可以对这个一定程度设定一个阀值 S S S,计算出 f [ 1 ⋯ S ] [ j ] f[1\cdots S][j] f[1S][j],对于阀值之内的数输出 f [ n ] [ u ] f[n][u] f[n][u],否则输出 f [ S ] [ u ] + n − S f[S][u]+n-S f[S][u]+nS
考虑证明:
假设当前 f [ i ] = { a , b , c , d , e , f } f[i]=\{a,b,c,d,e,f\} f[i]={a,b,c,d,e,f},显然有 a ≤ b ≤ c ≤ d ≤ e ≤ f a\le b\le c\le d\le e\le f abcdef,那么可以假设当前 a = b = c = d = e < < f a=b=c=d=e<<f a=b=c=d=e<<f,则重掷后的贡献就是 1 + 1 6 ( 5 e + f ) = 1 + f − 5 6 ( f − e ) 1+\frac{1}{6}(5e+f)=1+f-\frac{5}{6}(f-e) 1+61(5e+f)=1+f65(fe)
因此重掷之后 f ′ − e ′ = f − ( 1 + f − 5 6 ( f − e ) ) = 5 6 ( f − e ) − 1 f'-e'=f-(1+f-\frac{5}{6}(f-e))=\frac{5}{6}(f-e)-1 fe=f(1+f65(fe))=65(fe)1,即每次重掷都会使差减小 1 6 \frac{1}{6} 61,因此 O ( l o g ) O(log) O(log)轮后差就会缩小到 ≤ 1 \le1 1,之后答案会一直由重掷贡献。

关于阀值设定?

设定一个阀值这个思想在实数域上二分里也经常用到,不过我当时没打表打出来它应该设成 100 100 100
后来打了个表发现 50 50 50就可以了???然后交了一发只有 60 p t s 60pts 60pts???然后设成 1000 1000 1000也能过???看来以后在时间空间允许的时候还是把阀值开大点保险啊,实在不知道阀值也可以把阀值设大点这么瞎搞搞说不定就是正解??? 整挺好
以后遇到这种乍一看看不出明显结论的要学会针对小数据打表处理找规律
f i n e fine fine我是没看出来怎么打的表,然后发现就是设计出了 D P DP DP方程之后输出了 D P DP DP结果看的结论,还是 D P DP DP不熟啊

代码

因为有对于 f f f的预处理,后面又是直接取的 m a x max max,最后又调用的是 f [ S ] [ u ] f[S][u] f[S][u],所以相当于已经取了 f [ i ] [ u ] = m a x ( u ∗ k , ∑ j f [ i − 1 ] [ j ] 6 + 1 ) f[i][u]=max(u*k,\frac{\sum_{j}f[i-1][j]}{6}+1) f[i][u]=max(uk,6jf[i1][j]+1)

#include <bits/stdc++.h>
using namespace std;
const int S=100;
int T,n,u,k;
double f[S+1][7];
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')	f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
inline void sol(){
	n=read(),u=read(),k=read();
	memset(f,0,sizeof(f));
	for(int i=1;i<=6;++i) f[0][i]=k*i;
	for(int i=1;i<=S;++i){
		double p=0;
		for(int w=1;w<=6;++w) p+=f[i-1][w];
		p/=6;
		for(int w=1;w<=6;++w) f[i][w]=max(f[i-1][w],p+1);
	}
	double ans;
	if(n<=S) ans=f[n][u];
	else ans=f[S][u]+n-S;
	printf("%.10lf\n",ans);
}
signed main(){
	freopen("roll.in","r",stdin);
	freopen("roll.out","w",stdout);
	T=read();
	while(T--) sol();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值