B - Discovering Gold LightOJ - 1030(概率 DP)

B - Discovering Gold LightOJ - 1030(概率 DP)

题意

  1. n 个位置排成一排,第 i 个位置有 ai 数量的钱,一个人刚开始在第 1 个位置,开始摇一个六面色子,要到几走几步,
  2. 如果摇得点数致使动当前位置走到 > n 的位置的话,这个时候重新摇,
  3. 直到走到 n 位置游戏结束。

思路

  1. 第一种思路就是求的,从初始位置走到其他位置的概率,怎么求这个概率呢?就是一个地推方式的简单 dp,
  2. 那么我们所求的期望就是, ∑ i = 1 n \sum_{i=1}^{n} i=1n 第 i 位置金币 * 到 i 位置概率

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
void Run(int x = 0) {     
#ifdef ACM  //宏定义免注释 freopen
    if(! x) fre(); else Fre();
#endif
}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair
#define m_p make_pair
#define for_(i, s, e) for(ll i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(ll i = (ll)(e); i >= (ll)(s); i --)
#define memset(a, b, c) memset(a, (int)b, c);
#define size() size() * 1LL
#define sc scanf
#define pr printf
#define sd(a) sc("%lld", &a)
#define ss(a) sc("%s", a)
#define __  pr( "------------------------\n" );
#define ___ pr("\n------------------------\n");
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
/*=========================ACMer===========================*/
const ll mxn = 1e3;
db a[mxn], dp[mxn];


int main()
{
    Run();
    ll T, cas = 1; sd(T);
    while(T --)
    {
        ll n; sd(n);
        for_(i, 1, n) sc("%lf", &a[i]);

        memset(dp, 0, sizeof dp);
        dp[1] = 1;

        for_(i, 1, n)
        {
            ll mx = min(n, i + 6);
            for_(j, i + 1, mx)
            {
                dp[j] += dp[i] / (mx - i);
            }
        }

        db sum = 0;
        for_(i, 1, n) sum += dp[i] * a[i];

        pr("Case %lld: %.6f\n", cas ++, sum);
    }


    return 0;
}


思路

  1. 另一种方法就是倒着从后往前 dp,定义 dp [i] 为从 i 位置到 n 位置的期望,初始化的时候,每个位置的期望就是 a i a_i ai,
    1. 对于第 n 个位置:dp [n] = a [n],
    2. 对于第 n-1 位置,从 n-1 到 n 位置有 1 种方案,所以 d p [ n − 1 ] = d p [ n − 1 ] + ( d p [ n ] / 1 ) dp[n-1]=dp[n-1]+(dp[n]/1) dp[n1]=dp[n1]+(dp[n]/1)
    3. 对于第 n-2 位置,从 n-2 到 n 位置有 2 种方案,所以 d p [ n − 2 ] = d p [ n − 2 ] + ( d p [ n − 1 ] / 2 + d p [ n ] / 2 ) dp[n-2]=dp[n-2]+(dp[n-1]/2+dp[n]/2) dp[n2]=dp[n2]+(dp[n1]/2+dp[n]/2)
    4. d p [ i ] = d p [ i ] + ∑ j = 1 6 d p [ i + j ] / 6 dp[i]=dp[i]+\sum^6_{j=1}dp[i+j]/6 dp[i]=dp[i]+j=16dp[i+j]/6
  2. 最终答案及时 dp [1],
  3. 至于为什么倒着 dp,我确实可以参考下面的解释:

链接:https://www.cnblogs.com/6262369sss/p/8987216.html

为什么只有从后往前才会符合推理 因为从前往后的概率分布是不均匀的。 从 a [i-6] 到 a [i] 的概率并不是 1/6,因为 a
[i-6] 可以先到 a [i-5],再到 a [i],

所以实际上,a [i-6] 到 a [i] 的 概率是低于其他值的,因为其他值到 a [i] 的概率是要先算上 a [i-6] 没有成功抵达
a [i] 的概率。

不能以 1 开始,因为状态方程只能为 a [i]=1/6a[i+1]+ 1/6a[i+2]+……+1/6a [i+6] 假如我们令方程为
a[i]=1/6
a[i-1]+ 1/6a[i-2]+……+1/6a[i-6] a[2]=a[1]+a[2]
a [3]=0.5a [1]+0.5a [2]+a [3]=a [1]+0.5a [2]+a [3] 那么
a [4]=1/3a [1]+1/3a [2]+1/3a [3]+a [4]=a [1]+0.5a [2]+1/3a [3]+a [4] 然而实际上 a
[4] 的期望并不是这样 如果 1 到 4。 从 1 开始,到 2 的概率为 1/3,到 3 的概率为 1/3 加上 2 到 3 的概率,为
0.5。 所以正确的是 a [4]=a [1]+1/3a [2]+0.5a [3]+a [4]。

其实逆推可以保证一直是从里面推到外面而且概率是正确的。

因为先算 3->4 的得出 dp [3]=a [3]+a [4]

再算 2->4 的,得到 2->3 (然后再到 4), 以及 2->4,dp [2] = dp [3]/2 (2->3->4) + dp
[4]/2 (2->4) + a [2],这里面又用到 3->4 的期望 dp [3]

然后类比这个处理过程,每次都从内到外的计算,就不会出现概率不对的情况了。

就像进行数运算时先考虑最里面的括号,算出来答案,然后再考虑次里面的括号,由内到外的算出答案。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
void Run(int x = 0) {     
#ifdef ACM  //宏定义免注释 freopen
    if(! x) fre(); else Fre();
#endif
}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair
#define m_p make_pair
#define for_(i, s, e) for(ll i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(ll i = (ll)(e); i >= (ll)(s); i --)
#define memset(a, b, c) memset(a, (int)b, c);
#define size() size() * 1LL
#define sc scanf
#define pr printf
#define sd(a) sc("%lld", &a)
#define ss(a) sc("%s", a)
#define __  pr( "------------------------\n" );
#define ___ pr("\n------------------------\n");
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
/*=========================ACMer===========================*/
const ll mxn = 1e3;
db a[mxn], dp[mxn];


int main()
{
    Run();
    ll T, cas = 1; sd(T);
    while(T --)
    {
        ll n; sd(n);
        for_(i, 1, n) sc("%lf", &dp[i]);
        rep_(i, n, 1)
        {
            ll mx = min(n, i + 6);
            for_(j, i + 1, mx)
            {
                dp[i] += dp[j] / (mx - i);        
            }
        }

        pr("Case %lld: %.6f\n", cas ++, dp[1]);
    }


    return 0;
}


你可能感兴趣的:(概率,DP)