hdu 4035 概率DP 成都网赛

http://acm.hdu.edu.cn/showproblem.php?pid=4035

学到:

1、先判断是不是树,其实凡是有图的感觉的,都看边数==点数-1是不是成立  

2、树有时候区分老子跟孩子还是有必要的,这道题就是,不过是在dfs的时候,传参数的时候多加个表示父节点的参数而已

3、一定注意,概率DP对精度真的要求很高 开始的时候写1e-8,WA了好几发,改了1e-10  AC

4、注意分母为0的可能的时候加上判断


讲的很详细的题解:http://blog.csdn.net/morgan_xww/article/details/6776947

直接按公式写的代码就是:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <map>
#include <set>
#include <queue>
using namespace std;

#define ls(rt) rt*2
#define rs(rt) rt*2+1
#define ll long long
#define ull unsigned long long
#define rep(i,s,e) for(int i=s;i<e;i++)
#define repe(i,s,e) for(int i=s;i<=e;i++)
#define CL(a,b) memset(a,b,sizeof(a))
#define IN(s) freopen(s,"r",stdin)
#define OUT(s) freopen(s,"w",stdout)
const ll ll_INF = ((ull)(-1))>>1;
const double EPS = 1e-10;
const int INF = 100000000;
const int MAXN = 10000+100;

vector<int>g[MAXN];
double k[MAXN],e[MAXN];
double a[MAXN],b[MAXN],c[MAXN];
int n;

bool sea(int i, int fa)
{
    if(g[i].size() == 1 && fa!=-1)//叶子节点
    {
        a[i]=k[i];
        c[i]=b[i]=1.0-k[i]-e[i];
        return true;
    }
    //非叶子节点,此时该非叶子节点的子孙都已经遍历过了
    double aa=0.0,bb=0.0,cc=0.0;
    for(int j=0;j<g[i].size();j++)
    {
        if( g[i][j] == fa)continue;
        if(!sea(g[i][j],i))return 0;
        aa+=a[g[i][j]];
        bb+=b[g[i][j]];
        cc+=c[g[i][j]];
    }
    int m=g[i].size();
    a[i]=(k[i]+(1-k[i]-e[i])/m*aa)/(1-(1.0-k[i]-e[i])/m*bb);
    b[i]=(1.0-k[i]-e[i])/m/(1.0-(1.0-k[i]-e[i])/m*bb);
    c[i]=( (1.0-k[i]-e[i])+(1.0-k[i]-e[i])/m*cc )/(1.0 -(1.0-k[i]-e[i])/m*bb);
    return true;
}

int main()
{
    int ncase,u,v,ic=0;

    scanf("%d",&ncase);
    while(ncase--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            g[i].clear();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf",&k[i],&e[i]);
            k[i]/=100.0;
            e[i]/=100.0;
        }

        printf("Case %d: ",++ic);
        if(sea(1,-1) && fabs(1.0-a[1])>EPS)
            printf("%.6lf\n",c[1]/(1.0-a[1]));
        else
            printf("impossible\n");
    }
    return 0;
}

当然更好的写法还是题解上的

#include <cstdio>  
#include <iostream>  
#include <vector>  
#include <cmath>  
  
using namespace std;  
  
const int MAXN = 10000 + 5;  
  
double e[MAXN], k[MAXN];  
double A[MAXN], B[MAXN], C[MAXN];  
  
vector<int> v[MAXN];  
  
bool search(int i, int fa)  
{  
    if ( v[i].size() == 1 && fa != -1 )  
    {  
        A[i] = k[i];  
        B[i] = 1 - k[i] - e[i];  
        C[i] = 1 - k[i] - e[i];  
        return true;  
    }  
  
    A[i] = k[i];  
    B[i] = (1 - k[i] - e[i]) / v[i].size();  
    C[i] = 1 - k[i] - e[i];  
    double tmp = 0;  
      
    for (int j = 0; j < (int)v[i].size(); j++)  
    {  
        if ( v[i][j] == fa ) continue;  
        if ( !search(v[i][j], i) ) return false;  
        A[i] += A[v[i][j]] * B[i];  
        C[i] += C[v[i][j]] * B[i];  
        tmp  += B[v[i][j]] * B[i];  
    }  
    if ( fabs(tmp - 1) < 1e-10 ) return false;  
    A[i] /= 1 - tmp;  
    B[i] /= 1 - tmp;  
    C[i] /= 1 - tmp;  
    return true;  
}  
  
int main()  
{  
    int nc, n, s, t;  
  
    cin >> nc;  
    for (int ca = 1; ca <= nc; ca++)  
    {  
        cin >> n;  
        for (int i = 1; i <= n; i++)  
            v[i].clear();  
  
        for (int i = 1; i < n; i++)  
        {  
            cin >> s >> t;  
            v[s].push_back(t);  
            v[t].push_back(s);  
        }  
        for (int i = 1; i <= n; i++)  
        {  
            cin >> k[i] >> e[i];  
            k[i] /= 100.0;  
            e[i] /= 100.0;  
        }  
          
        cout << "Case " << ca << ": ";  
        if ( search(1, -1) && fabs(1 - A[1]) > 1e-10 )  
            cout << C[1]/(1 - A[1]) << endl;  
        else  
            cout << "impossible" << endl;  
    }  
    return 0;  
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值