期望DP的一些题

hdu3853

题意:

  1. 题意:有一个迷宫r行m列,开始点在[1,1]现在要走到[r,c] 
  2. 对于在点[x,y]可以打开一扇门走到[x+1,y]或者[x,y+1] 
  3. 消耗2点魔力 
  4. 问平均消耗多少魔力能走到[r,c] 

英语不好,只能看别人的翻译

网上题解有些没说清楚,不管是走还是停,都会花费两点魔力值。

很裸的期望DP,我记得有句很经典的话:终点是一切概率之末,确是一切期望之始。

很好懂的Dp方程式:

Dp[i][j]=(Dp[i+1][j]+2)*P1+(Dp[i][j+1]+2)*P2+(Dp[i][j]+2)*P0

注意到P1+P2+P0=1;移相即可化简

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,m;
double dp[1005][1005],p[1005][1005][3];
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        memset(dp,0,sizeof(dp));
        memset(p,0,sizeof(p));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                scanf("%lf %lf %lf",&p[i][j][0],&p[i][j][1],&p[i][j][2]);
            }
        dp[n][m]=0;
        for(int i=n;i>=1;i--)
            for(int j=m;j>=1;j--)
            {
                if(i==n&&j==m) continue;
                if(p[i][j][0]==1) continue;
                dp[i][j]=(p[i][j][1]*dp[i][j+1]+p[i][j][2]*dp[i+1][j]+2)/(1-p[i][j][0]);
            }    
        printf("%.3f\n",dp[1][1]);    
    }
    
}

hdu4405:这道题更加简单,不用考虑留下来的概率,该怎么做就怎么做

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,vis[100010];
double dp[100010];
int main()
{
    while(~scanf("%d %d",&n,&m)&&n)
    {
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            vis[a]=b;
        }
        for(int i=n-1;i>=0;i--)
        if(vis[i])  dp[i]=dp[vis[i]];else
        for(int j=1;j<=6;j++)
        dp[i]+=(dp[i+j]+1)/6.0;
        printf("%.4f\n",dp[0]);
    }
 } 

 hdu4336

其实懂了思路后很好写,反正就是常规套路。

ans=sigma(last_ans+cost)*probablity+ans*probablity of remain

#include<iostream>
#include<cstdio>
#include<algorithm>
double dp[1100005],p[1005];
int n;

using namespace std;
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%lf",&p[i]);
        }
        int l=(1<<n)-1;
        dp[l]=0;
        for(int sta=l-1;sta>=0;sta--)
        {
            double sum=0,tot=0;
            for(int j=1;j<=n;j++)
            if(!(sta&(1<<j-1)))
            sum+=p[j]*(dp[sta|(1<<j-1)]),tot+=p[j];
            sum+=1; 
            dp[sta]=sum/(tot);
        }
        printf("%.5f\n",dp[0]);
    }
}

 bzoj4832 

小Q同学现在沉迷炉石传说不能自拔。他发现一张名为克苏恩的牌很不公平。如果你不玩炉石传说,不必担心,小Q
同学会告诉你所有相关的细节。炉石传说是这样的一个游戏,每个玩家拥有一个 30 点血量的英雄,并且可以用牌
召唤至多 7 个随从帮助玩家攻击对手,其中每个随从也拥有自己的血量和攻击力。小Q同学有很多次游戏失败都是
因为对手使用了克苏恩这张牌,所以他想找到一些方法来抵御克苏恩。他去求助职业炉石传说玩家椎名真白,真白
告诉他使用奴隶主这张牌就可以啦。如果你不明白我上面在说什么,不必担心,小Q同学会告诉你他想让你做什么
。现在小Q同学会给出克苏恩的攻击力是 K ,表示克苏恩会攻击 K 次,每次会从对方场上的英雄和随从中随机选
择一个并对其产生 1 点伤害。现在对方有一名克苏恩,你有一些奴隶主作为随从,每名奴隶主的血量是给定的。
如果克苏恩攻击了你的一名奴隶主,那么这名奴隶主的血量会减少 1 点,当其血量小于等于 0 时会死亡,如果受
到攻击后不死亡,并且你的随从数量没有达到 7 ,这名奴隶主会召唤一个拥有 3 点血量的新奴隶主作为你的随从
;如果克苏恩攻击了你的英雄,你的英雄会记录受到 1 点伤害。你应该注意到了,每当克苏恩进行一次攻击,你
场上的随从可能发生很大的变化。小Q同学为你假设了克苏恩的攻击力,你场上分别有 1 点、 2 点、 3 点血量的
奴隶主数量,你可以计算出你的英雄受到的总伤害的期望值是多少吗?
 
 
 

Input

输入包含多局游戏。
第一行包含一个整数 T (T<100) ,表示游戏的局数。
每局游戏仅占一行,包含四个非负整数 K, A, B 和 C ,表示克苏恩的攻击力是 K ,你有 A 个 1 点血量的奴隶
主, B 个 2 点血量的奴隶主, C 个 3 点血量的奴隶主。
保证 K 是小于 50 的正数, A+B+C 不超过 7 。
这道题条件比较麻烦,直接写DP比较麻烦,用dfs+记忆化写方便
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
double vis[55][8][8][8],f[55][8][8][8];
int T,k,a,b,c;
double pro(int x,int a,int b,int c)
{
    return double(x)/(a+b+c+1);
}
double dp(int k,int a,int b,int c)
{
    if(!k) return 0;
    c=min(c,7-a-b);
    if(vis[k][a][b][c]) return f[k][a][b][c];
     
    vis[k][a][b][c]=1;
    f[k][a][b][c]+=pro(1,a,b,c)*(dp(k-1,a,b,c)+1);
    if(a)f[k][a][b][c]+=pro(a,a,b,c)*dp(k-1,a-1,b,c);
    if(b)f[k][a][b][c]+=pro(b,a,b,c)*dp(k-1,a+1,b-1,c+1);
    if(c)f[k][a][b][c]+=pro(c,a,b,c)*dp(k-1,a,b+1,c);
    return f[k][a][b][c];
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(vis,0,sizeof(vis));
        memset(f,0,sizeof(f));
        scanf("%d %d %d %d",&k,&a,&b,&c);
        printf("%.2f",dp(k,a,b,c));
    }
 } 

 bzoj1076

【bzoj1076】[SCOI2008]奖励关
2014年2月7日4,0871
Description

你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃)。 宝物一共有n种,系统每次抛出这n种宝物的概率都相同且相互独立。也就是说,即使前k-1次系统都抛出宝物1(这种情况是有可能出现的,尽管概率非常小),第k次抛出各个宝物的概率依然均为1/n。 获取第i种宝物将得到Pi分,但并不是每种宝物都是可以随意获取的。第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过一次,才能吃第i种宝物(如果系统抛出了一个目前不能吃的宝物,相当于白白的损失了一次机会)。注意,Pi可以是负数,但如果它是很多高分宝物的前提,损失短期利益而吃掉这个负分宝物将获得更大的长期利益。 假设你采取最优策略,平均情况你一共能在奖励关得到多少分值?

Input

第一行为两个正整数k和n,即宝物的数量和种类。以下n行分别描述一种宝物,其中第一个整数代表分值,随后的整数依次代表该宝物的各个前提宝物(各宝物编号为1到n),以0结尾。

Output

输出一个实数,保留六位小数,即在最优策略下平均情况的得分。

比较明显的壮压+DP的期望题

#include<cstdio>
#include<iostream>
using namespace std;
double F[101][65536];
int N,K,t;
int v[20],d[20],p[20];
int main()
{
    for(int i=1;i<=16;i++)p[i]=1<<(i-1);
    scanf("%d%d",&N,&K);
    for(int i=1;i<=K;i++)
    {
        scanf("%d%d",&v[i],&t);
        while(t)
        {
            d[i]+=p[t];
            scanf("%d",&t);
        }
    }
    for(int i=N;i;i--)
        for(int j=0;j<=p[K+1]-1;j++)
        {
            for(int k=1;k<=K;k++)
                if((d[k]&j)==d[k])
                   F[i][j]+=max(F[i+1][j],F[i+1][j|p[k]]+v[k]);
                else F[i][j]+=F[i+1][j];
            F[i][j]/=K;
        }
    printf("%.6lf",F[1][0]);
    return 0;
}

 

 

posted @ 2017-10-17 19:23  dancer16  阅读(365)  评论(0编辑  收藏  举报
描述