两道图上概率dp

1.2019icpc南京网络赛D Robot

题目链接:https://nanti.jisuanke.com/t/41301

题目大意:

有一个 n n n个点 m m m条边的 D A G DAG DAG, 第 i i i步的花费是 i i i, 问 D A G DAG DAG上从 1 t h 1_{th} 1th节点到 n t h n_{th} nth节点的期望花费。

题解思路:

概率dp的题目一般都是用 d p [ 1 ] dp[1] dp[1]表示答案,这题同样套用概率dp的模板,用 d p [ i ] dp[i] dp[i]表示从 i i i号点到 n n n号点的期望花费。
( p s : ps: ps:这样反向设可以避免后效性) 要反向建图。
在求 d p [ i ] dp[i] dp[i]之前,要先求出所有 i i i号节点的前驱,所以需要反向建完图之后跑一个拓扑排序。
再维护一个 d a y [ i ] day[i] day[i]表示 i i i号点到 n n n号点的期望天数
转移方程就是:

d a y [ u ] = ∑ ( d a y [ v ] + 1 ) + ( d a y [ u ] + 1 ) o u t [ u ] + 1 day[u]=\frac{\sum (day[v]+1) +(day[u]+1)}{out[u]+1} day[u]=out[u]+1(day[v]+1)+(day[u]+1)

d p [ u ] = ∑ ( d p [ v ] + d a y [ v ] + 1 ) + ( d p [ u ] + d a y [ u ] + 1 ) o u t [ u ] + 1 dp[u]=\frac{\sum (dp[v]+day[v]+1) +(dp[u]+day[u]+1)}{out[u]+1} dp[u]=out[u]+1(dp[v]+day[v]+1)+(dp[u]+day[u]+1)

解释一下,在原图中, u u u节点到 n n n的期望=所有( v v v n n n的期望 + v +v +v n n n的期望天数 + 1 +1 +1) ∗ * 转移到 v v v的概率
题目允许在自身停留,所有的情况就是 u u u的出度 + 1 +1 +1,在分子中再单独加上这种情况,最后单独提出 d p [ u ] dp[u] dp[u]就是转移方程

反向建边的话,出度改成入度就可以了

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define int ll
#define debug cout<<"fuck"<<endl;
#define pb  push_back
#define endl '\n'
#define fi first
#define se second
#define db double
#define pii pair<int,int>
#define mp make_pair
const int mod=(int)1e9+7;
const int maxn=(int)2e5+5;
int n,m;
vector<int>v;
queue<int>q;
vector<int>edg[maxn];
int in[maxn];
int inn[maxn];
db dp[maxn];
db day[maxn];
void solve()
{
    for(int i=1;i<=n;i++)
    {
        if(in[i]==0)q.push(i);
    }
    while(!q.empty())
    {
        int p=q.front();q.pop();
        v.pb(p);
        for(int i=0;i<edg[p].size();i++)
        {
            int y=edg[p][i];
            in[y]--;
            if(in[y]==0)q.push(y);
        }
    }
}
void init()
{
    memset(dp,0,sizeof(dp));
    memset(day,0,sizeof(day));
    while(!q.empty())q.pop();
    for(int i=0;i<=n;i++)
    {
        edg[i].clear();
    }
    v.clear();
    memset(in,0,sizeof(in));
}
int t;
signed main()
{
    IOS
    cin>>t;
    while(t--)
    {
        init();
        cin>>n>>m;
        int xx,yy;
        for(int i=1;i<=m;i++)
        {
            cin>>xx>>yy;
            edg[yy].pb(xx);
            in[xx]++;
        }
        memcpy(inn,in,sizeof(in));
        solve();
        //cout<<v[0]<<' '<<v.back()<<endl;
        day[n]=0,dp[n]=0;
        for(auto x:v)
        {
            if(x!=n)
            {
                day[x] += 1.0 / inn[x];
                dp[x] += (1.0 + day[x]) / inn[x];
            }
            for(auto y:edg[x])
            {
                day[y]+=(1.0+day[x])/inn[y];
                dp[y] +=(1.0+dp[x]+day[x])/inn[y];
            }
        }
        cout<<fixed<<setprecision(2)<<dp[1]<<endl;
    }
    return 0;
}

The 14-th BIT Campus Programming Contest L 旅行的意义

题目链接 https://codeforces.com/gym/102174/problem/L

题目大意

D A G DAG DAG 从1号点开始旅行,从 u u u号点有等概率向其后继转移,不同两点间的转移需要花费一天。或者至多在 u u u停留一天,问结束旅行时的期望天数

同样套用概率 d p dp dp的套路, d p [ 1 ] dp[1] dp[1]表示 1 1 1号点结束旅行的期望天数,由于是结束的期望,需要用 d f s dfs dfs来进行 d p dp dp数组的更新,先更新深度更深的期望天数,最后更新到 d p [ 1 ] dp[1] dp[1]

需要处理的地方就是至多停留一天这个条件
转移方程:

d p [ u ] = 1 + ∑ ( d p [ v ] + 1 ) ∗ ( i n v ( n u m + 1 ) + i n v ( n u m + 1 ) ∗ i n v ( n u m ) ) dp[u]=1+\sum (dp[v]+1)*(inv(num+1)+inv(num+1)*inv(num)) dp[u]=1+(dp[v]+1)(inv(num+1)+inv(num+1)inv(num))

解释一下, u u u v v v有两种方式转移,一个是在 u u u停留一天,一个是在 u u u停留两天。
转移完后再加上 i n v ( n u m + 1 ) inv(num+1) inv(num+1)

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define int ll
#define debug cout<<"fuck"<<endl;
#define pb  push_back
#define endl '\n'
#define fi first
#define se second
#define db double
#define pii pair<int,int>
#define mp make_pair
const int mod=(int)998244353;
const int maxn=(int)1e5+5;
int n,m,t;
int dp[maxn];
int vis[maxn];
vector<int>g[maxn];
ll qp(ll a,ll b,ll c)
{
    ll res=1;
    while(b)
    {
        if(b & 1)
        {res=(res*a)%c;}
        a=(a*a)%c;
        b=b>>1;
    }
    return res;
}
 
int inv(int x)
{
    return qp(x,mod-2,mod);
}
 
void dfs(int now)
{
    if(vis[now])
    {
        return;
    }
    vis[now]=1;
    int sz=(int)g[now].size();
    for(auto nxt:g[now])
    {
        dfs(nxt);
        dp[now] += (dp[nxt]+1) * ((inv(sz+1)+inv(sz+1)*inv(sz)%mod)%mod)%mod;
        dp[now] %= mod;
    }
    dp[now]+=inv(sz+1);
    dp[now]%=mod;
}
signed main()
{
    int x,y;
    IOS
    cin>>t;
    while(t--)
    {
        memset(vis,0,sizeof(vis));
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            dp[i]=1;
            g[i].clear();
        }
 
        while(m--)
        {
            cin>>x>>y;
            g[x].pb(y);
        }
        dfs(1);
        cout<<dp[1]<<endl;
    }
 
 
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
期望dp概率dp是两种不同的动态规划方法。 期望dp是指通过计算每个状态的期望值来求解最终的期望。在期望dp中,我们通常定义dp\[i\]表示在第i个状态时的期望值,然后通过状态转移方程来更新dp数组,最终得到最终状态的期望值。期望dp通常用于求解期望问题,例如求解骰子的期望点数、求解抽奖的期望次数等。 概率dp是指通过计算每个状态的概率来求解最终的概率。在概率dp中,我们通常定义dp\[i\]表示在第i个状态时的概率,然后通过状态转移方程来更新dp数组,最终得到最终状态的概率概率dp通常用于求解概率问题,例如求解抛硬币出现正面的概率、求解从一副牌中抽到红心的概率等。 总结来说,期望dp概率dp的区别在于它们所计算的是不同的值,期望dp计算的是期望值,而概率dp计算的是概率值。 #### 引用[.reference_title] - *1* [概率/期望dp专题](https://blog.csdn.net/qq_34416123/article/details/126585094)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [【动态规划】数学期望/概率DP/期望DP详解](https://blog.csdn.net/weixin_45697774/article/details/104274160)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值