概率DP学习笔记(一)

  今天学了一下概率DP,做了几个题,记录一下。

 HDU4405

  这个题目的意思就是,棋盘由0到N构成,每次投骰子能投1-6,如果位置大于等于N,那么就算是到了终点。此外有一些飞行点,到了这个点就会直接到所对应的飞行点,并且可以多次飞行,即如果a是飞到b的飞行点,b又是飞到c的飞行点,那么就可以认为到a点的时候直接飞到了c点。问题是给你一个棋盘大小和飞行点,问你结束游戏所需回合的期望值。
  首先这是一个求期望的,所以就用倒推就好,但是记得开始前用dfs处理一下飞行点的问题,因为存在多次飞行的情况不能直接来。

  代码如下

#include <bits/stdc++.h>
#define N 100100
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a,n) memset(a,n,sizeof(a))
#define fread freopen("in.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
using namespace std;
double changce[N];
map<int,int> mp,fin;
void dfs(int beg,int en){
    if(mp[en]==0){
        fin[beg]=en;
    }else{
        dfs(beg,mp[en]);
    }
}
void init(){
    for(map<int,int>::iterator a=mp.begin(),b=mp.end();a!=b;++a){
        dfs(a->first,a->second);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    int n,m,temp1,temp2;

    while(cin>>n>>m&&(n+m)){
        mp.clear();
        fin.clear();
        while(m--){
            cin>>temp1>>temp2;
            mp[temp1]=temp2;
        }
        init();
        for(int i=1;i<6;++i){
            changce[n+i]=0;
        }
        changce[n]=0;
        for(int i=n-1;i>=0;--i){
            changce[i]=1;
            for(int j=1;j<=6;++j){
                if(i+j<=n){
                    if(fin[i+j]==0){
                        changce[i]+=changce[i+j]/6;
                    }else{
                        changce[i]+=changce[fin[i+j]]/6;
                    }
                }
            }
        }
        cout<<fixed<<setprecision(4)<<changce[0]<<endl;
    }
    return 0;
}

POJ2096

一个软件有s个子系统,会产生n种bug

每天发现一个bug,这个bug属于一个子系统,属于一个分类。

每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n。

问发现n种bug且每个子系统都发现bug所需天数的期望。

是考虑系统、分类数而不考虑具体的是哪种分类,这个问题的抽象就很容易解决了

下面分析出自kuangbin博客

dp[i][j]表示已经找到i种bug,j个系统的bug,达到目标状态的天数的期望
dp[n][s]=0;要求的答案是dp[0][0];
dp[i][j]可以转化成以下四种状态:
 dp[i][j],发现一个bug属于已经有的i个分类和j个系统。概率为(i/n)*(j/s);
 dp[i][j+1],发现一个bug属于已有的分类,不属于已有的系统.概率为 (i/n)*(1-j/s);
 dp[i+1][j],发现一个bug属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(j/s);
 dp[i+1][j+1],发现一个bug不属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(1-j/s);

#include <iostream>
#include <iomanip>
#define N 1010
#define INF 0x3f3f3f3f
#define LL long long
#define mem(a,n) memset(a,n,sizeof(a))
#define fread freopen("in.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
using namespace std;
double dp[N][N];
int main()
{
    ios::sync_with_stdio(false);
    int s,n;
    while(cin>>n>>s){
        dp[n][s]=0;
        for(int i=n;i>=0;--i){
            for(int j=s;j>=0;--j){
                if(i==n&&j==s){
                    continue;
                }
                dp[i][j]=(i*(s-j)*dp[i][j+1]+
                         (n-i)*j*dp[i+1][j]+
                         (n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j);
            }
        }
        /*for(int i=0;i<=s;++i){
            for(int j=0;j<=n;++j){
                cout<<fixed<<setprecision(4)<<dp[i][j]<<' ';
            }
            cout<<endl;
        }*/
        cout<<fixed<<setprecision(4)<<dp[0][0]<<endl;
    }
    return 0;
}

HDU3853

  一个r*c的地图,在每个格子上都有对应的m1、m2、m3,在每一块格子上,下一回合有m1的几率不动,m2的几率下移,m3的几率右移,问你从(1,1)到(r,c)的所需步数期望

  这个也很裸,但是有个坑点,见代码

#include <bits/stdc++.h>
#define N 1010
#define INF 0x3f3f3f3f
#define LL long long
#define eps 1e-9
#define mem(a,n) memset(a,n,sizeof(a))
#define fread freopen("in.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
using namespace std;
double mp[N][N][3];
double dp[N][N];
int main()
{
    //ios::sync_with_stdio(false);
    int r,c;
    while(cin>>r>>c){
        dp[r-1][c-1]=0; 
        for(int i=0;i<r;++i){
            for(int j=0;j<c;++j){
                scanf("%lf%lf%lf",mp[i][j],mp[i][j]+1,mp[i][j]+2);//这里使用cin会WA,不明白为什么
            }
        }
        for(int i=r-1;i>=0;--i){
            for(int j=c-1;j>=0;--j){
                if(i==r-1&&j==c-1){
                    continue;
                }if(fabs(mp[i][j][0]-1.00)<1e-8)
                    continue;
                dp[i][j]=(dp[i+1][j]*mp[i][j][2]+dp[i][j+1]*mp[i][j][1]+2)/(1-mp[i][j][0]);
            }
        }
        /*for(int i=0;i<r;++i){
            for(int j=0;j<c;++j){
                cout<<fixed<<setprecision(3)<<dp[i][j]<<' ';
            }
            cout<<endl;
        }*/
        cout<<fixed<<setprecision(3)<<dp[0][0]<<endl;
    }
    return 0;
}

CSU1725

  这个题严格说来不是概率DP而是记忆化搜索,不过是顺便练了就凑进来了

  中文题目,所以就不解释题意了,直接上代码吧

#include <bits/stdc++.h>
#define N 25
#define INF 0x3f3f3f3f
#define LL long long
#define eps 1e-9
#define mem(a,n) memset(a,n,sizeof(a))
#define fread freopen("in.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)
using namespace std;
double dp[N][N][8][8][8];
void init();
double dfs(int x,int y,int a,int b,int c);
int main()
{
        ios::sync_with_stdio(false);
        int t,x,y,a,b,c;
        cin>>t;
        while(t--){
            cin>>x>>y>>a;
            init();
            cout<<fixed<<setprecision(6)<<dfs(x,y,a,0,0)<<endl;
        }
        return 0;
}
void init()
{
    for(int i=0;i<N;++i){
        for(int j=0;j<N;++j){
            for(int k=0;k<8;++k){
                for(int l=0;l<8;++l){
                    for(int o=0;o<8;++o){
                        dp[i][j][k][l][o]=-1;
                    }
                }
            }
        }
    }
}
double dfs(int x,int y,int a,int b,int c)
{
    if(dp[x][y][a][b][c]!=-1){
        return dp[x][y][a][b][c];
    }
    if(y==0){
        return dp[x][y][a][b][c]=1.0;
    }
    if(x<y||!x){
        dp[x][y][a][b][c]=0;
    }else{
        dp[x][y][a][b][c]=1.0/(a+b+c+1)*dfs(x-1,y-1,a,b,c);
        if(a+b+c<7){
            if(a>0){
                dp[x][y][a][b][c]+=1.0*a/(a+b+c+1)*dfs(x-1,y,a,b+1,c);
            }
            if(b>0){
                dp[x][y][a][b][c]+=1.0*b/(a+b+c+1)*dfs(x-1,y,a+1,b-1,c+1);
            }
        }else{
            if(a>0){
                dp[x][y][a][b][c]+=1.0*a/(a+b+c+1)*dfs(x-1,y,a-1,b+1,c);
            }
            if(b>0){
                dp[x][y][a][b][c]+=1.0*b/(a+b+c+1)*dfs(x-1,y,a,b-1,c+1);
            }
        }
        if(c>0){
            dp[x][y][a][b][c]+=1.0*c/(a+b+c+1)*(x-1,y,a,b,c-1);
        }
    }
    return dp[x][y][a][b][c];
}

关于为什么一般期望倒推,概率顺推的思考(一)

  我一开始猜想是不是因为类似0-1背包要从二维优化到一维的时候一样,要保证考虑的子问题的解是不会重复迭代的,拿0-1背包举例

//错误的代码
for(int i=cost;i<volume;++i){
    dp[i]=max(dp[i-cost]+value,dp[i]);
}
//正确的代码
for(int i=volume;i>=cost;--i){
    dp[i]=max(dp[i-cost]+value,dp[i]);
}

  看代码可以直接想到,如果用前一个代码,那么在这一轮循环中可能出现一个物品拿了多次的情况,这就是被重复迭代了的结果,那么我们类似地通过代码考虑一下期望顺推的情况,这里以第一题为例。

  但是我写了一下代码……

  并不是这样的……

  然后问了一下学长,如果是期望的情况,推导的就是由终了状态到起始状态的逆推,而终了状态到终了状态的期望是0,建立在这样的基础上逆推就是正确的答案。

  而概率的话,终了状态的信息位置,无法逆推。

​  之后再补完,感觉还是没有完全弄懂。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值