ACM概率期望dp刷题总结

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Dylan_Frank/article/details/77887683

这个周刷了很多概率期望有关的dp题目,缘起2016青岛D题和取log的神操作题HDU 5988 2016青岛区域赛 (最小费用流)

这类题目没有固定的模板,而且概率可以很容易插入一些经典模型,比如下面的 TSP。最小费用流。
我刷的这部分题目,无一例外均可用dp解决,主要是找到状态,很多题目都可以抽象成马尔科夫链

下面记录一下刷的经典题目

B - Discovering Gold

一排1到n的格子,每个格子上有黄金 ai ,你最开始在 1 号,每一次投塞子决定到哪一个格子,不能超出范围,你到了哪个格子就得到哪个格子的金币,问最终在n 能得到金币的期望

思路

在第i 个格子能转移到 i+j,i+jnj6, 转移概率为能转移的格子数。,求出每个格子的概率,用概率乘上金币数目就是期望.

code

#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-10
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 100+10;
const int MAX_V= 500+10;
const int MOD = 998244353;

double dp[maxn];
int a[maxn];
int main()
{
    // ios_base::sync_with_stdio(0);
    // cin.tie(0);
    // cout.tie(0);

    int T;
    cin>>T;
    int kase =0;
    while (T--) {
        int n;
        cin>>n;
        ms(dp,0);
        double ans =0;
        for(int i =1 ; i<=n ; ++i)cin>>a[i];
        dp[1] = 1;
        for(int i=1 ; i <= n ; ++i){
            int k = min(6,n-i);
            for(int j =1 ; j<=k ; ++j)dp[j+i]+= dp[i]/k;
        }
        for(int i=1 ; i<=n ; ++i){
            ans += dp[i]*a[i];
        }
        printf("Case %d: %.9lf\n",++kase, ans);
    }


    //std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
    return 0;
}

Island of Survival

(感谢A_LeiQ)翻译
被拉去参加一个野外求生……姑且这么叫吧。
岛上有t只老虎(T)和d只熊(D)还有自己(M)。每天会有两只生物相遇(自己也算)
T-M T会吃掉M
T-D T会吃掉D
D-D Nothing
M-D M可以选择杀与不杀D
T-T 两只T会互相残杀(Two Die)

问最终人类可以存货的概率(人类存活是指老虎都死光)

思路

这个问题其实很简单,遇到deer 不杀就行了,简单的想就是,杀了熊以后,老虎遇到你的概率就更大了,
dp[i][j 表示剩余 ij deer 时存活概率,用以上情况简单递推就好

#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-10
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 1000+10;
const int MAX_V= 500+10;
const int MOD = 998244353;

double dp[maxn][maxn];

void dfs(int t, int d){

    if(t!=0){
        dp[t -2][d]+= dp[t][d] * t/(t+d+1)*(t-1)/(t+d);
        dfs(t-2,d);
    }
    if(d!=0){
        dp[t][d-1]+= dp[t][d]*2*t/(t+d+1)*d/(t+d);
        dfs(t,d-1);
    }
}
int main()
{
    // ios_base::sync_with_stdio(0);
    // cin.tie(0);
    // cout.tie(0);

    int T;
    cin>>T;
    int kase =0;
    while (T--) {
        int t,d;
        scanf("%d%d", &t,&d);
        // if(t ==0 ){
        //     printf("Case %d: %.9lf\n",++kase, 1.0);continue;
        // }
        // if(t & 1){
        //     printf("Case %d: %.9lf\n",++kase, 0.0);continue;
        //
        // }
        ms(dp,0);
        dp[t][d] = 1;
        for(int tt = t ; tt>=0 ; tt--){
            for(int dd = d ; dd >=0 ; dd--){
                int sum = tt*(tt-1)/2 + tt*dd + tt;
                if(sum==0)continue;
                if(tt>=2)dp[tt -2][dd]+= dp[tt][dd] * (tt-1)*tt/2/sum;
                if(dd)dp[tt][dd-1]+= dp[tt][dd]*tt*dd/sum;
            }
        }
        double ans =0;
        for(int i=0; i<=d ; ++i)ans +=dp[0][i];
        printf("Case %d: %.11lf\n",++kase, ans);
    }


    //std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
    return 0;
}

注意求总的情况数的时候,deer遇到deer不会影响

Batting Practice

这个题是很经典的马尔科夫链模型,

投球,投不中的概率是 p,连续投中 k1 或者连续投不中 k2 次,结束,问投球次数的期望.

思路

fi 表示连续 i 次没投中,gi 表示连续 i 次投中,则 fi 能转移到 fi+1,p 或者 g1,1p,则fi 的期望关系有

fi=p(fi+1+1)+(1p)(g1+1)

同理有 gi 解出f1,g1就好

code

#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-8
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 2e3+10;
const int MAX_V= 500+10;
const int MOD = 998244353;
int main()
{

   int T;
   scint(T);
   int kase =0;
   while (T--) {
      double p;
      int m,n;
      scanf("%lf%d%d",&p,&m,&n );
      if(p < eps){
            printf("Case %d: %.9lf\n",++kase, m*1.0);continue;
      }else if(1-p < eps) {
            printf("Case %d: %.9lf\n",++kase, n*1.0);continue;
      }
      double q = 1-p;
      double k1 = (1.0 - pow(q,m-1.0))/p,k2 = (1.0-pow(p,n-1))/q;
      double f1 = k1*(1.0+k2*p)/(1.0-k1*k2*p*q);
      double g1 = k2*(q*f1+1.0);
      double ans = 1.0+ q*f1+p*g1;
      printf("Case %d: %.9lf\n",++kase, ans);
   }



   return 0;
}

Aladdin and the Magical Sticks

阿拉丁捡木棍,这题神坑,网上有 O(n) 的做法,但我一直不能理解.模型是邮票问题的扩展捡到一张可区分木根后其余木根的概率和均不可区分时应该不一样吧,为撒任然用不可区分时的模板来算?????????

dp做法

我是用的dp 做法
由于不可区分木根之间捡到的概率无差别,可区分也是一样,求出平均权重w1,w2 ,
dp[i][j] 表示分别捡到 i,j根可区分和不可区分木根时的期望。则

dp[i][j]=(ni)/s(dp[i+1][j]+w1)+(mj)/s(dp[i][j+1]+w2)+j/s(dp[i][j]+w2)

解出dp[i][j],其中s 表示在 i,j 点剩余能捡到木根总数 n,m 分别表示可区分,和不可区分木根总数

code

#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-10
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 5005;

double dp[2][maxn];
int main()
{

    // ios_base::sync_with_stdio(0);
    // cin.tie(0);
    // cout.tie(0);
    int T;
    cin>>T;
    int kase =0;
    while (T--) {
        int N;scint(N);
        double w1 = 0,w2 = 0;
        int n =0,m =0;
        for(int i=0 ; i<N ; ++i){
            int w,num;scanf("%d%d",&w,&num );
            if(num==1)w1+=w,n++;
            else w2 += w,m++;
        }
        if(n)w1/=n;
        if(m)w2/=m;
        //std::cout << w1<<" " << w2 << '\n';
        ms(dp,0);
        for(int i = n ; i>=0 ; --i){
            for(int j = m ; j>=0 ; --j){
                if(i == n && j == m)continue;
                int s = n+m - i;
                //std::cout << s << '\n';
                dp[i%2][j] = (n-i)*(dp[(i+1)%2][j]+w1) + (m-j)*(dp[i%2][j+1]+w2) + j*w2;
                dp[i%2][j] /= (s-j);
                //std::cout << "dp["<<i<<"]["<<j<<"] ="<<dp[i][j]<< '\n';
            }
        }
        printf("Case %d: %.9lf\n",++kase,dp[0][0] );
    }
    //std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
    return 0;
}

Where to Run TSP 问题

这个题的题目意思有点坑
感谢Flying_Fatty提供翻译和题解.

code

#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define PI acos(-1)
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define INF64 0x3f3f3f3f3f3f3f3f
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define ms(x,v) memset((x),(v),sizeof(x))
#define scint(x) scanf("%d",&x );
#define scf(x) scanf("%lf",&x );
#define eps 1e-6
#define dcmp(x) (fabs(x) < eps? 0:((x) <0?-1:1))
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;
typedef long double DB;
typedef pair<int,int> Pair;
const int maxn = 15;
int n,m;
int vis[1<<maxn][maxn];
double dp[1<<maxn][maxn];

int G[maxn][maxn];

bool dfs(int s,int u){
    if(s == (1<<n) - 1){
        dp[s][u] = 0;return 1;
    }
    if(vis[s][u])return dp[s][u] > eps;//可达
    vis[s][u] =1;
    int cnt =0;
    dp[s][u] = 5;
    for(int i =0 ; i<n ; ++i){
        if(G[u][i] && !(s&(1<<i)) && dfs(s|(1<<i),i)){
            int ns = s|(1<<i);
            dp[s][u] += G[u][i] + dp[ns][i];
            cnt++;
        }
    }
    if(cnt)dp[s][u]/=cnt;
    else dp[s][u] = 0;
    return cnt >0;
}

int main()
{

    // ios_base::sync_with_stdio(0);
    // cin.tie(0);
    // cout.tie(0);
    int T;
    cin>>T;
    int kase =0;
    while (T--) {
        ms(vis,0);
        ms(dp,0);
        ms(G,0);
        scanf("%d%d",&n,&m );
        while (m--) {
            int u,v,t;
            scanf("%d%d%d", &u,&v,&t);
            G[u][v] = G[v][u] = t;
        }
        dfs(1,0);
        printf("Case %d: %.9lf\n",++kase,dp[1][0] );
    }
    //std::cout << "time "<< clock()/1000 <<"ms"<< '\n';
    return 0;
}
展开阅读全文

没有更多推荐了,返回首页