概率dp专场
第一题--poj3744 Scout YYF I 链接 (简单题)
算是递推题 如果直接推的话 会TLE 会发现 在两个长距离陷阱中间 很长一部分都是重复的 我用 a表示到达i-2步的概率 b表示到达i-1步的概率 c表示到达i步的概率
如果数很大的话 中间肯定会有重复的a,b,c 直接将i挪到最近的陷阱前一位 i = a[o]-1,大大节省时间。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define LL long long 12 #define INF 0xfffffff 13 const double eps = 1e-8; 14 const double pi = acos(-1.0); 15 const double inf = ~0u>>2; 16 int x[12]; 17 int main() 18 { 19 int i,n,j; 20 double p; 21 while(cin>>n) 22 { 23 cin>>p; 24 for(i = 0 ;i < n ;i++) 25 { 26 cin>>x[i]; 27 } 28 sort(x,x+n); 29 double a = 1.0,b=0.0,c; 30 double ta = 0,tb = 0; 31 int o = 0; 32 if(x[0]==1) 33 a = 0; 34 else a = 1; 35 for(i = 2; i <= x[n-1]+1 ; i++) 36 { 37 if(i==x[o]) 38 { 39 o++; 40 while(x[o]==x[o-1]) 41 o++; 42 c = 0; 43 } 44 else 45 c = a*p+b*(1.0-p); 46 b = a; 47 a = c; 48 if(o<n&&fabs(ta-a)<eps&&fabs(tb-b)<eps) 49 i = x[o]-1; 50 ta = a; 51 tb = b; 52 } 53 printf("%.7f\n",c); 54 } 55 return 0; 56 }
第二题--poj3071Football(简单题)
dp[i][j] 表示第i场j赢的概率 那么可以写出方程dp[i][j] += dp[i-1][g]*p[j][g].
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 100000 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double p[130][130]; 18 double dp[130][130]; 19 int pp[12]; 20 int main() 21 { 22 int i,j,n,g; 23 pp[0] = 1; 24 for(i = 1 ;i <= 10 ;i++) 25 pp[i] = pp[i-1]*2; 26 while(cin>>n) 27 { 28 if(n==-1) break; 29 memset(dp,0.0,sizeof(dp)); 30 int k = n; 31 n = pp[n]; 32 for(i = 1; i <=n ; i++) 33 for(j = 1 ;j <= n; j++) 34 cin>>p[i][j]; 35 for(i = 1; i <=n ;i++) 36 { 37 if(i%2) 38 dp[1][i] = p[i][i+1]; 39 else 40 dp[1][i] = p[i][i-1]; 41 } 42 for(i = 2 ;i <= k ;i++) 43 { 44 for(j = 1 ;j <= n ;j+=pp[i]) 45 { 46 for(g = j; g < j+pp[i-1] ; g++) 47 { 48 for(int e = j+pp[i-1] ; e < j+pp[i] ; e++) 49 { 50 dp[i][g]+=dp[i-1][g]*dp[i-1][e]*p[g][e]; 51 dp[i][e]+=dp[i-1][e]*dp[i-1][g]*p[e][g]; 52 } 53 } 54 } 55 } 56 double maxz = 0; 57 int ans; 58 for(i = 1; i <= n ;i++) 59 { 60 if(maxz<dp[k][i]) 61 { 62 maxz = dp[k][i]; 63 ans = i; 64 } 65 //printf("%.5lf %d\n",dp[k][i],i); 66 } 67 cout<<ans<<endl; 68 } 69 return 0; 70 }
第三题--CodeForces 148D(简单题)
刚开始看错了题意,以为一次跳出一个老鼠,按概率直接算,WA后发现dragon画的时候会另有一只跳出来,这样就根据跳出来的是黑还是白分两种情况计算。
如果当前是dragon轮 也就是(总数-剩余数)%3!=0 时 dp[i][j] = j/(i+j)*((j-1)/(i+j-1)*dp[i][j-2]+i/(i+j-1)*dp[i-1][j-1])
dp[i][j] 表示还有i只白老鼠及j只黑老鼠时princess赢的概率。 princess轮计算方式类似。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 1010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N][N]; 18 int main() 19 { 20 int i,a,b,j; 21 while(cin>>a>>b) 22 { 23 memset(dp,0,sizeof(dp)); 24 for(i = 1 ;i <= a ; i++) 25 { 26 if((a+b-i)%3==0) 27 dp[i][0] = 1; 28 } 29 for(i = 1 ; i <= a ;i++) 30 for(j = 1 ;j <= b ;j++) 31 { 32 if((a+b-(i+j))%3) 33 { 34 if(j>=2) 35 dp[i][j] = j*1.0/(i+j)*((j-1)*1.0/(i+j-1)*dp[i][j-2]+(i*1.0)/(i+j-1)*dp[i-1][j-1]); 36 else 37 dp[i][j] = j*1.0/(i+j)*dp[i-1][j-1]; 38 } 39 else 40 { 41 dp[i][j] = i*1.0/(i+j)+j*1.0/(i+j)*dp[i][j-1]; 42 } 43 //cout<<i<<" "<<j<<" "<<dp[i][j]<<endl; 44 } 45 printf("%.9lf\n",dp[a][b]); 46 } 47 return 0; 48 }
第四题--POJ 2151 Check the difficulty of problems
这个题正推不好推,可以求逆,dp[i][j][g]表示第i个队在前g个题里解决了j道题的概率 再令开一dd[i][j]累加和保存第i队最多解决了j道题的概率
这样依次求出每队解决大于1道题的概率减掉每队解决少于n道的概率 即为每队至少解决一道并且冠军队解决至少n道的概率
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 1010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-12; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N][45][45],dd[N][45]; 18 double p[N][45]; 19 int main() 20 { 21 int i,j,n,m,t,g,e; 22 while(cin>>m>>t>>n) 23 { 24 if(!n||!m||!t) break; 25 memset(dp,0.0,sizeof(dp)); 26 memset(dd,0.0,sizeof(dd)); 27 for(i = 1; i <= t ; i++) 28 { 29 dp[i][0][0] = 1.0; 30 for(j = 1; j <= m ;j++) 31 { 32 cin>>p[i][j]; 33 dp[i][0][j] = (1.0-p[i][j])*dp[i][0][j-1]; 34 } 35 dd[i][0] = dp[i][j][m]; 36 } 37 for(i = 1 ;i <= t ;i++) 38 for(j = 1 ;j <= m ;j++) 39 { 40 e = 1 ; 41 for(g = j ;g <= m ; g++) 42 { 43 dp[i][j][g] = dp[i][j-1][g-1]*p[i][g]+dp[i][j][g-1]*(1-p[i][g]); 44 } 45 dd[i][j] = dd[i][j-1]+dp[i][j][m]; 46 } 47 double ans = 1.0,ss=1.0; 48 for(i = 1; i <= t; i++) 49 { 50 ans*=dd[i][m]-dd[i][0]; 51 ss*=dd[i][n-1]-dd[i][0]; 52 } 53 printf("%.3f\n",ans-ss); 54 } 55 return 0; 56 }
第五题--POJ 2096 Collecting Bugs (简单期望题)
学习完这题可以了解到这一类期望题的解法,确定好一个边界状态,可能是初态也可能是终态,然后向后或向前推。
这题可以确定的为终态,dp[n][s] = 0. 然后倒推就可以了 dp[i][j] = dp[i][j+1]*(s-j)*1.0/s*i/n+dp[i+1][j]*(n-i)/n*j/s+dp[i+1][j+1]*(n-i)/n*(s-j)/s+1+i*j*1.0/n/s;
一个方程一个未知数,dp[0][0]即为答案。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 1010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N][N]; 18 int main() 19 { 20 int n,s,i,j; 21 while(cin>>n>>s) 22 { 23 memset(dp,0.0,sizeof(dp)); 24 for(i = n ; i >= 0 ;i--) 25 for(j = s ; j >= 0 ;j--) 26 { 27 if(i==n&&j==s) continue; 28 dp[i][j] = dp[i][j+1]*(s-j)*1.0/s*i/n+dp[i+1][j]*(n-i)/n*j/s+ 29 dp[i+1][j+1]*(n-i)/n*(s-j)/s+1; 30 dp[i][j] = dp[i][j]/(1-(i*j*1.0/n/s)); 31 32 } 33 printf("%.4f\n",dp[0][0]); 34 } 35 return 0; 36 }
与上一题类似,可以确定终态 dp[r][c] = 0.然后根据可走的概率进行倒推,注意可能会有无解的情况,p[i][j][0]=1就会进入死循环。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 1010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N][N]; 18 double p[N][N][3]; 19 int main() 20 { 21 22 int i,j,r,c; 23 while(cin>>r>>c) 24 { 25 memset(dp,0.0,sizeof(dp)); 26 for(i = 1; i <= r ; i++) 27 { 28 for(j = 1; j <= c; j++) 29 { 30 for(int g = 0 ; g < 3 ; g++) 31 scanf("%lf",&p[i][j][g]); 32 } 33 } 34 for(i = r ; i >= 1 ; i--) 35 { 36 for(j = c ; j >= 1 ; j--) 37 { 38 if(i==r&&j==c) continue; 39 dp[i][j] = dp[i+1][j]*p[i][j][2]+dp[i][j+1]*p[i][j][1]+2; 40 if(fabs(1-p[i][j][0])<eps) continue; 41 dp[i][j] = dp[i][j]/(1-p[i][j][0]); 42 } 43 } 44 printf("%.3lf\n",dp[1][1]); 45 } 46 return 0; 47 }
第七题--HDU 4405 Aeroplane chess (简单期望题)
投掷骰子的问题,也是一样的问题,确定终态求dp[0]。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 101000 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 int f[N]; 18 double dp[N]; 19 int main() 20 { 21 int n,m,i,j; 22 while(cin>>n>>m) 23 { 24 if(!n&&!m) break; 25 memset(f,0,sizeof(f)); 26 memset(dp,0,sizeof(dp)); 27 int x,y; 28 for(i = 1 ;i <=m ; i++ ) 29 { 30 cin>>x>>y; 31 f[x] = y; 32 } 33 for(i = n-1 ; i >= 0 ; i--) 34 { 35 if(f[i]) 36 dp[i] += dp[f[i]]; 37 else 38 { 39 for(j = 1 ;j <= 6; j++) 40 dp[i] += dp[i+j]*1.0/6; 41 dp[i]+=1; 42 } 43 } 44 printf("%.4f\n",dp[0]); 45 } 46 return 0; 47 }
第八题--ZOJ 3640 Help Me Escape (简单期望题)
这个题是d的战斗力值 也就是当战斗力为多少的时候可以确定一个终态 ,很显然当值为maxz+1 的时候 它最大可达2*maxz.
这样就可以写出递推方程 dp[i] = (dp[i+c[j]]+1)*1/n;(第j出口所需战斗力》=i) dp[i] += day[j] (第j出口所需战斗力<i)
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 20010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 int c[105]; 18 double dp[N]; 19 int main() 20 { 21 int n,f,i,j; 22 while(cin>>n>>f) 23 { 24 memset(dp,0,sizeof(dp)); 25 for(i = 0; i < n ;i++) 26 cin>>c[i]; 27 sort(c,c+n); 28 for(i = 2*c[n-1] ; i >= f ; i--) 29 { 30 int o = 0,k=0; 31 double t = 0,tt=0; 32 for(j = 0 ; j < n ;j++) 33 { 34 if(i>c[j]) 35 { 36 int d = (1.0+sqrt(5.0))/2*c[j]*c[j]; 37 t+=1.0/n*d; 38 } 39 else 40 { 41 k++; 42 if(c[j]==0) o++; 43 else 44 t+=1.0/n*(dp[i+c[j]]+1); 45 } 46 } 47 if(o&&fabs(1-o*1.0/n)>eps) 48 dp[i] += (t+1)/(1-o*1.0/n); 49 dp[i] += t; 50 } 51 printf("%.3f\n",dp[f]); 52 } 53 return 0; 54 }
H第九题--DU 4336 Card Collector(简单期望题)
状压一下,然后确定终态倒推。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 20010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[1<<20],p[21]; 18 int main() 19 { 20 int n,i,j,g; 21 while(scanf("%d",&n)!=EOF) 22 { 23 memset(dp,0,sizeof(dp)); 24 for(i = 0 ;i < n; i++) 25 scanf("%lf",&p[i]); 26 for(j = (1<<n)-1 ; j>=0 ; j--) 27 { 28 double t = 0; 29 double o = 0; 30 for(g = 0; g < n ;g++) 31 if((1<<g)&j) continue; 32 else 33 { 34 o+=p[g]; 35 t+=p[g]*dp[j+(1<<g)]; 36 } 37 if(fabs(o)>eps) 38 dp[j] = (t+1)/o; 39 } 40 printf("%f\n",dp[0]); 41 } 42 return 0; 43 }
第十题--SGU 495 Kids and Prizes(简单期望题)
正推题,这个题比较巧妙,可以确定初始态dp[1] = 1,因为第一个人拿到球的概率就为1,那么第i个拿到球的期望就为,他拿到球的概率*(dp[i-1]+1),他拿到球的概率就等于有球的房间/总房间
那么有球的房间就为n-dp[i-1],因为期望就为球数,所以dp[i-1]就为拿走的球数。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 100010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N]; 18 int main() 19 { 20 int i,n,m; 21 while(cin>>n>>m) 22 { 23 memset(dp,0,sizeof(dp)); 24 dp[1] = 1; 25 for(i = 2; i <= m ;i++) 26 dp[i] = (dp[i-1]+1)*(1-dp[i-1]/n)+dp[i-1]*dp[i-1]/n; 27 printf("%.10f\n",dp[m]); 28 } 29 return 0; 30 }
第十一题--ZOJ 3329 One Person Game(简单期望题)
也是比较好推的题目,相对前面来说会多一个未知数,但是推到最后就会发现只有一个未知数,一个方程一个未知数,可以求解。
可以把每一步的期望分为2部分保存,可以准确算出的用dp[i]表示 依旧是未知的用o[i]保存他的系数,留到最后求解。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 1010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N],p[N],o[N]; 18 int main() 19 { 20 int i,j,n,k1,k2,k3,g,a,b,c,t; 21 cin>>t; 22 while(t--) 23 { 24 memset(dp,0,sizeof(dp)); 25 memset(p,0,sizeof(p)); 26 memset(o,0,sizeof(o)); 27 cin>>n>>k1>>k2>>k3>>a>>b>>c; 28 int k = k1+k2+k3; 29 for(i = 1; i <= k1 ; i++) 30 for(j = 1;j <= k2 ; j++) 31 for(g = 1; g <= k3 ; g++) 32 { 33 if(i==a&&j==b&&g==c) continue; 34 p[i+j+g]+=1.0/(k1*k2*k3); 35 } 36 for(i = n ; i >= 0 ; i--) 37 { 38 for(j = 3; j <= k ; j++) 39 { 40 dp[i]+=dp[i+j]*p[j]; 41 o[i]+=p[j]*o[i+j]; 42 } 43 dp[i]+=1; 44 o[i]+=1.0/(k1*k2*k3); 45 } 46 if(fabs(1-o[0])>eps) 47 dp[0] = dp[0]/(1-o[0]); 48 printf("%.14f\n",dp[0]); 49 } 50 return 0; 51 }
有两种询问,一种一个推法,n个相同的话,dp[i]表示最后有连续i个相同的到最后有n个相同的点数的期望,dp[i] = 0,dp[i] = 1.0/m*dp[i+1]+1+(m-1)/m*dp[1];
另开一数组保存dp[1] 的系数,最后求解。
n个两辆不同,与之类似 dp[i] = ((m-i)*1.0/m*dp[i+1]+1)+1.0/m*dp[1]+1.0/m*dp[2]+....+1.0/m*dp[i]; 化简一下,每次可以消掉一个未知数,求到dp[1]的时候自然只剩了一个未知数。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 1000010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N],o[N]; 18 int main() 19 { 20 int i,t,n,m,k; 21 while(cin>>t) 22 { 23 while(t--) 24 { 25 memset(dp,0,sizeof(dp)); 26 memset(o,0,sizeof(o)); 27 scanf("%d%d%d",&k,&m,&n); 28 if(k==0) 29 { 30 for(i = n-1 ; i >= 1 ; i--) 31 { 32 dp[i] = 1.0/m*dp[i+1]+1; 33 o[i] = 1.0/m*o[i+1]+(m-1)*1.0/m; 34 } 35 dp[1] = dp[1]/(1-o[1]); 36 dp[0] = dp[1]+1; 37 printf("%.12f\n",dp[0]); 38 } 39 else 40 { 41 for(i = n-1 ;i >= 1 ; i--) 42 { 43 dp[i] = ((m-i)*1.0/m*dp[i+1]+1)/(1-(m-i)*1.0/m*o[i+1]-1.0/m); 44 o[i] = ((m-i)*1.0/m*o[i+1]+1.0/m)/(1-(m-i)*1.0/m*o[i+1]-1.0/m); 45 } 46 dp[0] = dp[1]+1; 47 printf("%.12f\n",dp[0]); 48 } 49 } 50 } 51 return 0; 52 }
树上求期望的题,其实也与之类似,仔细想下会发现叶子节点的期望状态比较好确定,因为它只有一个父亲节点所以这样写出来的方程会只有2个未知数,一个是父亲节点一个是1号节点,到最后父亲节点肯定只有1,所以又会变成一个方程一个未知数。
dp[i]表示第i节点到满足结果的期望。
i为叶子节点 dp[i] = p1i*dp[1]+p2i*dp[fa[i]]+p3i*exit(exit==0)
这样可以多开两个数组存两个未知数的系数。
i为非叶子节点 dp[i] = p1i*dp[1]+p2i*1/k*dp[v](v与i相连的节点,共有K个)+p3i*exit。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 #include<vector> 11 using namespace std; 12 #define N 100010 13 #define LL long long 14 #define INF 0xfffffff 15 const double eps = 1e-10; 16 const double pi = acos(-1.0); 17 const double inf = ~0u>>2; 18 vector<int>ed[N]; 19 double dp[N],p[N][3]; 20 double o[N][2]; 21 int flag; 22 void dfs(int u,int pre) 23 { 24 if(!flag) return ; 25 int i; 26 dp[u] = p[u][2]; 27 o[u][0] = p[u][2]; 28 o[u][1] = p[u][0]; 29 int k = ed[u].size(); 30 double pp = 0; 31 for(i = 0; i < k ; i++) 32 { 33 int v = ed[u][i]; 34 if(v==pre) continue; 35 dfs(v,u); 36 dp[u]+=p[u][2]/k*dp[v]; 37 o[u][1]+=p[u][2]/k*o[v][1]; 38 pp+=p[u][2]/k*o[v][0]; 39 } 40 if(u==1) 41 { 42 if(fabs(1-(o[u][1]+pp))>eps) 43 dp[u] = dp[u]/(1-(o[u][1]+pp)); 44 else 45 flag = 0; 46 return; 47 } 48 if(fabs(1-pp)>eps) 49 { 50 dp[u] = (dp[u])/(1-pp); 51 o[u][1] = (o[u][1])/(1-pp); 52 o[u][0] = p[u][2]/k/(1-pp); 53 } 54 else 55 { 56 flag = 0; 57 return ; 58 } 59 } 60 int main() 61 { 62 int i,j,t,n; 63 int kk = 0; 64 cin>>t; 65 while(t--) 66 { 67 memset(dp,0,sizeof(dp)); 68 memset(o,0,sizeof(o)); 69 scanf("%d",&n); 70 flag = 1; 71 for(i = 1 ;i <= n ;i++) 72 ed[i].clear(); 73 for(i = 1; i < n ;i++) 74 { 75 int u,v; 76 scanf("%d%d",&u,&v); 77 ed[u].push_back(v); 78 ed[v].push_back(u); 79 } 80 for(i = 1; i <=n ;i++) 81 { 82 int x,y; 83 scanf("%d%d",&x,&y); 84 p[i][0] = x/100.0; 85 p[i][1] = y/100.0; 86 p[i][2] = 1-p[i][1]-p[i][0]; 87 } 88 dfs(1,-1); 89 printf("Case %d: ",++kk); 90 if(flag) 91 printf("%f\n",dp[1]); 92 else 93 puts("impossible"); 94 } 95 return 0;
第十四题--HDU 4418 Time travel(高斯消元求期望)
这个题意吧有点难理解。。是这个时光机每次可以走1-m步 概率分别为p[i]。
为了好做,可以加n-2个点统一方向, 0 1 2 3 2 1
这样dp[i] = (dp[i+1]+1)*p[i+1]+(dp[i+2]+2)*p[i+2]+...(dp[i+m]+m)*p[i+m];
这样会发现无论你倒推还是正推都无法减少未知数,不过可以列出来n-2个方程,n-2个未知数,高斯消元。
需要先bfs出是否能够达到,然后保留能够达到的点。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 205 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double p[N],a[N][N],ans[N]; 18 int n,m,y,x,d,dis[N],g; 19 int zn; 20 bool vis[N]; 21 void gauss(int zw,int zr) 22 { 23 int i,j,k,g = 0; 24 for(k = 0 ; k < zw && g < zr; k++,g++) 25 { 26 i = k; 27 for(j = k+1 ; j <= zw ; j++) 28 { 29 if(fabs(a[j][g])>fabs(a[i][g])) 30 i = j; 31 } 32 if(fabs(a[i][g])<eps) 33 { 34 continue; 35 } 36 if(i!=k) 37 for(j = k ;j <= zr ; j++) 38 swap(a[i][j],a[k][j]); 39 for(i = k+1 ; i <= zw ; i++) 40 { 41 if(fabs(a[i][k])<eps) continue; 42 double s = a[i][g]/a[k][g]; 43 a[i][g] = 0.0; 44 for(j = g+1 ; j <= zr; j++) 45 a[i][j] -= s*a[k][j]; 46 } 47 } 48 for(i = zw ; i >= 0 ; i--) 49 { 50 if(fabs(a[i][i])<eps) continue; 51 double s = a[i][zn]; 52 for(j = i+1 ; j <= zw ;j++) 53 s-=a[i][j]*ans[j]; 54 ans[i] = s/a[i][i]; 55 } 56 } 57 int bfs() 58 { 59 int i; 60 queue<int>q; 61 memset(vis,0,sizeof(vis)); 62 q.push(x); 63 vis[x] = 1; 64 int ff = 0; 65 while(!q.empty()) 66 { 67 int u = q.front(); 68 q.pop(); 69 if(u==y||zn-u==y) 70 { 71 ff = 1; 72 continue; 73 } 74 for(i = 1; i <= g; i++) 75 { 76 int v = (u+dis[i])%zn; 77 if(!vis[v]) 78 { 79 vis[v] = 1; 80 q.push(v); 81 } 82 } 83 } 84 return ff; 85 } 86 int main() 87 { 88 int t,i,j; 89 cin>>t; 90 while(t--) 91 { 92 memset(a,0,sizeof(a)); 93 memset(ans,0,sizeof(ans)); 94 scanf("%d%d%d%d%d",&n,&m,&y,&x,&d); 95 zn = 2*n-2; 96 g = 0; 97 double sum = 0; 98 for(i= 1; i <= m; i++) 99 { 100 int pp; 101 scanf("%d",&pp); 102 if(pp>0) 103 { 104 p[++g] = pp/100.0; 105 dis[g] = i; 106 sum+=p[g]*i; 107 } 108 } 109 if(d>0) 110 x = zn-x; 111 if(x==y) 112 { 113 puts("0.00"); 114 continue; 115 } 116 if(!bfs()) 117 { 118 puts("Impossible !"); 119 continue; 120 } 121 for(i = 0 ; i < zn ; i++) 122 { 123 if(!vis[i]) continue; 124 a[i][i] = 1; 125 if(i==y||y==zn-i) continue; 126 for(j = 1 ; j <= g ;j++) 127 { 128 int dd = (dis[j]+i)%zn; 129 a[i][dd]-=p[j]; 130 } 131 a[i][zn] += sum; 132 } 133 gauss(zn-1,zn); 134 printf("%.2f\n",ans[x]); 135 } 136 return 0; 137 }
第十五题--HDU 4089 Activation(中等概率题)
这个题因为会出现循环求所以有求期望的感觉。
分情况看一下 dp[i][j]表示队里有i个人 他在第j的位置到最终结果的概率。
j==1 dp[i][1] = p1*dp[i][1]+p2*dp[i][i]+p4.
k>=j>1 dp[i][j] = p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1]+p4;
j>k dp[i][j] = p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1];
因为一个i循环里面只有一个dp[i][i]事未知的,可以开一个一维数组保存dp[i][j]里面还有dp[i][i]的系数。
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<vector> 7 #include<cmath> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 #define N 2010 12 #define LL long long 13 #define INF 0xfffffff 14 const double eps = 1e-8; 15 const double pi = acos(-1.0); 16 const double inf = ~0u>>2; 17 double dp[N][N],o[N]; 18 int main() 19 { 20 int i,j,n,m,k; 21 double p1,p2,p3,p4; 22 while(cin>>n>>m>>k) 23 { 24 scanf("%lf%lf%lf%lf",&p1,&p2,&p3,&p4); 25 memset(dp,0,sizeof(dp)); 26 memset(o,0,sizeof(o)); 27 int flag = 1; 28 if(fabs(1-p2-p1)<eps) 29 flag = 0; 30 else 31 dp[1][1] = p4/(1-p2-p1); 32 for(i = 2 ;i <= n ;i++) 33 { 34 o[0] = 0; 35 for(j = 1 ; j <= i; j++) 36 { 37 if(j<=k) 38 { 39 if(j==1) 40 { 41 dp[i][j]+=p4; 42 o[j] = p2; 43 } 44 else 45 { 46 dp[i][j] += p4+p2*dp[i][j-1]+p3*dp[i-1][j-1]; 47 o[j] = p2*o[j-1]; 48 } 49 } 50 else 51 { 52 dp[i][j]+= p2*dp[i][j-1]+p3*dp[i-1][j-1]; 53 o[j] = p2*o[j-1]; 54 } 55 if(j==i) 56 { 57 if(fabs(1-p1-o[j])<eps) 58 { 59 flag = 0; 60 break; 61 } 62 dp[i][j] = dp[i][j]/(1-p1-o[j]); 63 } 64 else 65 { 66 if(fabs(1-p1-o[j])<eps) 67 { 68 flag = 0; 69 break; 70 } 71 dp[i][j] = dp[i][j]/(1-p1); 72 o[j] = o[j]/(1-p1); 73 } 74 } 75 if(!flag) break; 76 for(j = 1;j < i ;j++) 77 dp[i][j]+=dp[i][i]*o[j]; 78 } 79 if(flag) 80 printf("%.5f\n",dp[n][m]); 81 else 82 printf("%.5f\n",0.0); 83 } 84 return 0; 85 }
第十六题--ZOJ 3380 Patchouli's Spell Cards(中等题)
这题没有推出来。。。 组合题,
dp[i,j] 表示用前i个数字在m个里放了j个位置,这些数字不一定都有用到
dp[i,j] = ∑ dp[i-1,j-k]*C[m-(j-k),k] 0≤k≤j , k<l
最后答案为dp[n,m]
个人认为做法很棒。。
1 import java.text.*; 2 import java.io.*; 3 import java.util.*; 4 import java.math.*; 5 import java.applet.*; 6 public class Main 7 { 8 public static void main(String[] args) 9 { 10 Scanner cin = new Scanner(System.in); 11 BigInteger [][]dp = new BigInteger[105][105]; 12 BigInteger o = BigInteger.valueOf(0); 13 BigInteger [][]c = new BigInteger[105][105]; 14 int n,m,i,j,l,k; 15 for(i=0;i<101;i++)c[i][0]=c[i][i]=BigInteger.valueOf(1); 16 for(i=2;i<101;i++){ 17 for(j=1;j<i;j++) 18 c[i][j]=c[i-1][j-1].add(c[i-1][j]); 19 } 20 while(cin.hasNext()) 21 { 22 m = cin.nextInt(); 23 n = cin.nextInt(); 24 l = cin.nextInt(); 25 BigInteger s1 ,s2 = BigInteger.valueOf(0); 26 s1 = BigInteger.valueOf(n); 27 s1 = s1.pow(m); 28 if(l>m) 29 { 30 System.out.println("mukyu~"); 31 continue; 32 } 33 else if(l>m/2) 34 { 35 for(i=l;i<=m;i++) 36 { 37 s2=s2.add(c[m][i].multiply( BigInteger.valueOf(n-1).pow(m-i) )); 38 } 39 s2=s2.multiply(BigInteger.valueOf(n)); 40 } 41 else 42 { 43 for(i = 0; i <= n ;i++) 44 for(j = 0; j <= m; j++) 45 dp[i][j] = o; 46 dp[0][0] = BigInteger.ONE; 47 for(i = 1; i <= n ;i++) 48 { 49 for(j = 0; j <= m ;j++) 50 { 51 for(k = 0; k <= Math.min(j, l-1) ;k++) 52 dp[i][j] = dp[i][j].add(dp[i-1][j-k].multiply(c[m-(j-k)][k])); 53 } 54 } 55 56 s2 = dp[n][m]; 57 s2 = s1.subtract(s2); 58 } 59 BigInteger gc = s2.gcd(s1); 60 System.out.println(s2.divide(gc)+"/"+s1.divide(gc)); 61 } 62 } 63 }