https://vjudge.net/contest/306858#problem/B
题意
有n个格子,每个格子上都有一定的黄金值;还有一个色子(1-6)。起始位置站在格子1上面,若每次投掷色子得到数x,x+i<=n(i表示现处位置的格子编号),则可以到达(x+i)格子上;反之,再进行一次投掷。问:到达标号为n的格子上面,得到黄金的期望值是多少?
思路
逆推期望,dp[i]表示现在你在i号,到终点所能得到黄金的期望(一般求期望都这样设),则dp[n]为a[n](a[i]表示i号格子黄金值),dp[n-1]=1.0 * dp[n]+a[n-1],dp[n-2]=0.5 * dp[n-1]+0.5*dp[n]+a[n-2],这样往前推就可以了。距终点大于6的格子,概率都为1.0/6.0。
代码
#include
#include
#include
using namespace std;
int a[110];
double dp[110];
int main()
{
int t,n,cnt=0;
scanf("%d",&t);
while(t--)
{
cnt++;
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
dp[n]=(double)a[n];
for(int i=n-1; i>=1; i--)
{
int tmp=n-i;
if(tmp<6)
{
double p=1.0/(double)tmp;
for(int j=1; j<=tmp; j++)
dp[i]+=p*dp[i+j];
dp[i]+=(double)a[i];
}
else
{
for(int j=1; j<=6; j++)
dp[i]+=dp[i+j]/6.0;
dp[i]+=(double)a[i];
}
}
printf("Case %d: %.12f\n",cnt,dp[1]);
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=4405
题意
在0-n格子上掷色子,从0点出发,掷了多少前进几步,同时有些格点直接相连,即若a,b相连,当落到a点时直接飞向b点。求走到n或超出n期望掷色子次数
思路
还是逆推期望,dp[n]=0,dp[n-1]=dp[n]/6.0+dp[n+1]/6.0+…+dp[n+5]/6.0+1…
若在i格子可以瞬移到a[i],则dp[i]=dp[a[i]]。从后往前推的,dp[a[i]]肯定已经求完。
代码
#include
#include
#include
#include
using namespace std;
double dp[100020];
int a[100020];
int main()
{
int n,m,x,y;
while(~scanf("%d%d",&n,&m))
{
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
if(!n&&!m)
break;
for(int i=0; i=0; i--)
{
if(a[i])
dp[i]=dp[a[i]];
else
{
for(int j=1; j<=6; j++)
dp[i]+=dp[i+j]/6.0;
dp[i]+=1.0;
}
}
printf("%.4f\n",dp[0]);
}
return 0;
}
https://vjudge.net/contest/306858#problem/C
题意
给一个数n,不断选择他的一个因子除以这个因子,直到除成1,问期望的操作次数,共1e4组数据,1<=n<=1e5
思路
自己独立做出来的第一道概率dp题,虽然很水,但高兴啊~~~
从特殊情况开始(开始或结束),e[1]=0,e[2]=1.0/2.0+1.0/2.0(1.0+e[2]),打个表,t组数据直接输出就可以了
解释一下e[2],2有1和2两个因子,选其中一个的概率是1.0/2.0,如果选2,则一次成功;如果选1,则又变回2,所以期望为(e[2]+1)
公式e[i]=(num+ans)/(num-1),num因子数,ans除去1和本身的因子和
代码
#include
#include
#include
using namespace std;
double e[100005];
void init()
{
double ans,num;
e[1]=0.0;
for(int i=2; i<=100000; i++)
{
ans=0.0;
num=2.0;
int tmp=sqrt(i);
for(int j=2; j<=tmp; j++)
{
if(i%j==0)
{
ans+=(e[j]+e[i/j]);
num+=2.0;
}
}
if(tmp*tmp==i)
{
ans-=e[tmp];
num--;
}
e[i]=(ans+num)/(num-1.0);
}
}
int main()
{
init();
int t,n;
int cnt=0;
scanf("%d",&t);
while(t--)
{
cnt++;
scanf("%d",&n);
printf("Case %d: %.8f\n",cnt,e[n]);
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=6558
题意
1.起初物品掉落率q=2%;
2.玩家进行一轮游戏,有p的概率获胜;
3.如果获胜了,有q的概率掉落一个叫Beta Pack的物品,终止;如果没得到Beta Pack,更新q=min(1,q+2%),返回2;
4.如果没获胜,更新q=min(1,1+1.5%),返回2;
求得到一个Beta Pack物品,进行游戏场次的期望。
题解
这题首先要分清,获胜和得到物品这两个事件,p是获胜概率,q是获胜之后得到物品的概率。
还是逆推概率,dp[i]表示q=i时还需进行的场次。
有p的概率获胜,当q=1时,只要获胜即可得到物品,那么有dp[1]=p+(1.0-p)*(dp[1]+1),移项得dp[1]=1.0/p
dp[i]后继状态有三种情况,pq的概率获胜并得到物品结束,p(1-q)的概率获胜但没得到物品更新q为min(1,q+2%),(1-p)的概率没获胜更新q为min(1,q+1.5%),可得方程
dp[i] = pq + p(1-q)(dp[i+2%]+1) + (1-p)(dp[i+1.5%]+1)
因为i代表概率,可以乘1000化成整数算。[1000,1020)区间先初始化,或者在dp方程中用min函数限制也可以。for循环,从后往前推就可以了,
代码
#include
#include
using namespace std;
double dp[1100];
int main()
{
int t,cnt=0;
double p;
scanf("%d",&t);
while(t--)
{
cnt++;
scanf("%lf",&p);
p/=100.0;
for(int i=1000; i<=1020; i++)
dp[i]=1.0/p;
for(int i=999; i>=20; i--)
{
dp[i]=p*(double)i/1000.0+(dp[i+20]+1)*p*(1.0-(double)i/1000.0)+(dp[i+15]+1)*(1.0-p);
}
printf("Case %d: %.10lf\n",cnt,dp[20]);
}
return 0;
}