ZLOJ练习51A & HDU - 5781 ATM Machine

written on 2022-07-27

概率与期望的题目,尤其是设置了情境的好像是模拟题的这类题,应当优先考虑 dp。

往 dp 的方向上想,首先考虑状态的设计。题目中有几个变量:

  1. 钱的上限。

  2. 当前的警告次数。

好像就这两个,每次取的钱数可以在转移的时候体现出来。

那么我们可以设 \(f_{i,j}\) 表示当前钱的上限为 \(i\),被警告次数为 \(j\) 的最小期望次数。

然后考虑边界,显然我们有 \(f_{0,0}=1\),然后的话其他的全部设为无穷大,由于是浮点数,所以尽量别用 memset

接着考虑转移,对于每一个状态,枚举每次选的钱数 \(k\)。那么对于 \(f_{i,j}\),有两种决策的可能性:

  1. 实际钱数小于 \(k\)

此时转移为 \(f_{i,j}=f_{k-1,j-1}\times ...+1\)

  1. 实际钱数大于等于 \(k\)

此时转移为 \(f_{i,j}=f_{i-k,j}\times ...+1\)

整合一下两种情况,由于钱数等概率,因此乘上的值只要乘以对应的比率即可,这样的转移就显然了。

额外的问题:即使是预处理出 dp 数组,时间复杂度也达到了 \(O(k^2\times w)\) 的级别,需要优化。

此时我们考虑其现实意义,只要每次取其中点,可以发现最多被警告 \(\log w\) 次即可,因此我们将 \(w\)\(13(\log w)\)\(\min\),这样时间复杂度就是 \(O(k^2\times \log w)\),能过。

Code

#include<bits/stdc++.h>
using namespace std;
double f[2005][13];
int main()
{
	for(int i=1;i<=2000;i++) for(int j=0;j<=12;j++) f[i][j]=1e9;
	for(int i=1;i<=12;i++) f[0][i]=0;
	for(int i=1;i<=2000;i++)
		for(int k=1;k<=12;k++)
			for(int j=1;j<=i;j++)
				f[i][k]=min(f[i][k],f[j-1][k-1]*j*1.0/(i+1)+f[i-j][k]*(i-j+1)*1.0/(i+1)+1);
	int K,W;
	while(scanf("%d%d",&K,&W)!=EOF) printf("%.6lf\n",f[K][min(W,12)]);
}

PS:个人感觉这题略有些问题,为了保证不超过 \(w\) 次这个前提好像根本没有考虑?但是 dp 写下来感觉又很顺,子状态的转移感觉也没什么问题,希望未来能有某位大佬看到然后帮忙解答疑惑。

posted @ 2022-07-31 22:40  Freshair_qprt  阅读(25)  评论(0编辑  收藏  举报