您的位置:首页 > 其它

poj3557 一个很巧妙的概率dp

2013-10-27 20:10 302 查看
题目大意很简单,但是做起来实在很难。

最开始看到N很小,想到的是状压dp,dp[i][st]表示考虑前i个点连出去的所有边,st是状态,如果某个点和1点连通,则此位为1

然后转移就枚举i点往连的所有边的可能性...

这样做复杂度为N*2^N*2^N...明显过不了

然后实在想不出看了别人的做法,居然是N^2的!!

别人的解释看得不怎么懂,自己YY了一个解释:

dp[i]表示i个点连通的概率。

直接算dp[i]实在太困难,那么我们算i个点不连通的概率。

先选定一个点p,从这个点出发来看,这个图不连通,则说明图上存在点不和p连通。

设和p连通的点有j个,那么这个图上有C(i-1,j-1)种和有j个点p连通的情况(从剩下的i-1个点中选j-1个点与p连通),每一种情况都有dp[j]的可能连通。

然后就要算这j个点与剩下的i-j个点都不连通的概率了(如果这j个点与剩下的任何1个点连通的话,这个情况就在j+1的时候考虑):(1-p)^(j*(i-j)),乘起来累加就是存在点和p不连通的概率,非p就是不存在点和p不连通,就是所有点都和p连通的概率了~

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=22;
int N;double p;
double dp[maxn];
double pp[maxn*maxn];
int C[maxn][maxn];

void pre()
{
for(int i=1;i<maxn;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++)
{
C[i][j]=C[i][j-1]*(i-j+1)/j;
}
}
}

void cal()
{
pp[0]=1;
for(int i=1;i<N*N;i++)
pp[i]=pp[i-1]*(1-p);
}

int main()
{
//  freopen("in.txt","r",stdin);
pre();
while(~scanf("%d%lf",&N,&p))
{
cal();
dp[1]=1;
for(int i=2;i<=N;i++)
{
dp[i]=0;
for(int j=1;j<i;j++)
{
dp[i]+=C[i-1][j-1]*dp[j]*pp[j*(i-j)];
}
dp[i]=1-dp[i];
}
printf("%.5f\n",dp
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: