概率dp入门解题报告两则

概率dp入门解题报告两则

刚接触概率dp,看过各位大佬的解题报告后不禁感慨,即便是最简单的dp和其他的一些小技巧结合起来也会变得难以理解(大概是我太蔡了吧…)。对着源码琢磨了数小时后终于获得了一些理解,下面和同学们讲解一下我对这两题的理解。
/ * 这是第一题 * /
POJ 2096 Collecting Bugs
题目大意:有一位程序员寻找bug,有n种bug存在于s个子系统中,问其集齐所有bug的期望。
解题思路:这题比较简单,根据题意写出状态转移方程即可。
总共有4种可能:在同一类,同一种;不在同一类,在同一种;在同一类,不在同一种;不在同一类,也不在同一种。
dp[i][j] = dp[i][j] * ( i / n) * ( j / s ) + dp[i+1][j] * ( ( n - i ) / n) * ( j / s ) + dp[i][j+1] * ( i / n ) * ( ( s - j ) / s ) + dp[i+1][j+1] * ( ( n - i ) / n ) * ( ( s - j ) / s ) +1;
化简得:
dp[i][j] = ( dp[i+1][j] * ( ( n - i ) * j ) + dp[i][j+1] * ( n * ( s - j ) )+ dp[i+1][j+1] * ( ( n - i ) * ( s - j ) ) + n * s )/(n * s - i * j);
代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
double dp[1009][1009];
int main(){
    
    
	int n,s;
	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]=(dp[i+1][j]*((n-i)*j)+dp[i][j+1]*(i*(s-j))+dp[i+1][j+1]*((n-i)*(s-j))+n*s)/(n*s-i*j);
			}
		}
		printf("%.4lf\n",dp[0][0]);
	}
	return 0;
}

/ * 这是第二题 * /
POJ 3071 Football
题目大意:有2 ^ n支足球队,通过锦标赛的模式竞选出冠军,有n组数据,每组数据有一个2 ^ n *2 ^ n 的矩阵来表示每队对上其他队的胜率,输出成为第一名概率最高的队伍。
解题思路:此题巧妙在位运算的表示,用(j>>(i-1))==(k>>(i-1)^1)来表示决胜的两队,此处的精妙我希望读者自己体会,一定会比我直接说出来更有价值(我也是这么过来的嘻)。
状态转移方程:dp[i][j]+=dp[i-1][j]*dp[i-1][k]*p[j][k];
p[j][k]表示j队战胜k队的概率;
dp[i-1][k]表示k队存活的可能;
dp[i-1][j]表示就队存活的可能。
代码如下

#include<bits/stdc++.h>
using namespace std;
#define ll long long
 
#define MAXN 150
double dp[MAXN][MAXN], p[MAXN][MAXN];
 
int main(){
    
    
    int n, num;
    while(~scanf("%d", &n) && n!=-1)
    {
    
    
    	memset(dp,0,sizeof(dp));
        int num=1<<n;
		for(int i=0;i<num;++i){
    
    
			dp[0][i]=1;
		} 
		for(int i=0;i<num;++i){
    
    
			for(int j=0;j<num;++j){
    
    
				scanf("%lf",&p[i][j]);
			}
		}
		for(int i=1;i<=n;++i){
    
    
			for(int j=0;j<num;++j){
    
    
				for(int k=0;k<num;++k){
    
    
					if((j>>(i-1))==(k>>(i-1)^1)){
    
    
						dp[i][j]+=dp[i-1][j]*dp[i-1][k]*p[j][k];
					}
				}
			}
		}
		int ans=0;
		for(int i=0;i<num;++i){
    
    
			if(dp[n][i]>dp[n][ans]){
    
    
				ans=i;
			}
		}
		printf("%d\n",ans+1);
    }
    return 0;
}

总结:这两题虽然不难吧…也够刚涉及竞赛的同学喝一壶了(其实是我太蔡了…)希望能对大家有所帮助。

猜你喜欢

转载自blog.csdn.net/skl4869/article/details/115533915