由于快要交概率论文了,先刷刷概率题,然后糊弄一篇上去就算了= =
自己挂了一场概率比赛,链接为http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=17303#overview,密码为yejinru
若以下有什么地方是错误的,欢迎指出^_^
Problem A | Football |
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int X = 130;
double dp[8][X];
double a[X][X];
int main(){
freopen("sum.in","r",stdin);
int h;
while(cin>>h,h!=-1){
int n = 1<<h;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
scanf("%lf",&a[i][j]);
for(int i=0;i<n;i++)
dp[0][i] = 1;
for(int i=1;i<=h;i++){
for(int j=0;j<n;j++){
int len = 1<<(i-1);
int s = ((j>>(i-1))^1)<<(i-1);
//cout<<i<<" "<<j<<" "<<len<<" "<<s<<endl;
dp[i][j] = 0;
for(int k=s;k<s+len;k++)
dp[i][j] += dp[i-1][k]*a[j][k];
dp[i][j] *= dp[i-1][j];
}
}
int pos = 0;
for(int i=0;i<n;i++)
if(dp[h][i]>dp[h][pos])
pos = i;
cout<<1+pos<<endl;
}
return 0;
}
Problem H | HDU 3853 | LOOPS |
题目:
给出一幅二维格子地图,然后每次会有三种去向(消耗均为2),概率分别为pr(向右),pd(向下),p(原地),现在某人从格子[1,1]出发,问到达[n,m]的期望消耗是多少
分析:
根据期望公式,不难想到期望公式如下: dp[i][j]表示从[i,j]出发到达[n,m]的期望消耗。
dp[i][j] = (dp[i][j]+2)*p[i][j]+(dp[i+1][j]+2)*pd[i][j]+(dp[i][j+1]+2)*pr[i][j];
由于等式右面有dp[i][j],移项后即可解出答案dp[1][1]。注意当a[i][j] == 1时,该格子不能再往下走,所以得要特殊处理。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int X = 1005;
double d[X][X],r[X][X],a[X][X];
double dp[X][X];
int n,m;
int main(){
freopen("sum.in","r",stdin);
while(cin>>n>>m){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lf%lf%lf",&a[i][j],&r[i][j],&d[i][j]);
memset(dp,0,sizeof(dp));
for(int i=n;i;i--)
for(int j=m;j;j--){
double temp = 0;
if(a[i][j]<1){
temp = (2+dp[i+1][j])*d[i][j]+(2+dp[i][j+1])*r[i][j]+2*a[i][j];
temp /= 1-a[i][j];
}
dp[i][j] = temp;
}
printf("%.3lf\n",dp[1][1]);
}
return 0;
}
Problem K | UVA 11021 | Tribles |
题目:
第一天的时候,有k个小鸟(Trible),她只能活一天,在她死之前,生下i个小鸟的概率为p[i],问第m天(包括m天之前)全死亡的概率。(小鸟最多能够生下n-1个后代)
分析:
独立概率问题,由于k只小鸟相互独立,所以求出一只小鸟以及她的后代在m天之内全死亡的概率dp[m],答案就为dp[m]^k。
考虑:
m = 1,概率为p[0],即没有生下后代
m = i,若第一天小鸟生下了i个后代的时候,即这i个小鸟得要在第i天都得死,这i个小鸟以及她们的后代只能够存活i-1天,有全概率公式,
dp[i] = p[0]+p[1]*f[i-1]+p[2]*f[i-1]^2+...+p[n-1]*f[i-1]^(n-1)
考虑p[j]*f[i-1]^j,表示第一天的小鸟生下了j个后代,要在i天全死,所以这j个后代又分别考虑,把第二天看成这j个小鸟的第一天,相当于子问题,然后得要在i-1天死亡,且j个小鸟相互独立,所以递推公式(dp[i] = p[0]+p[1]*f[i-1]+p[2]*f[i-1]^2+...+p[n-1]*f[i-1]^(n-1))很容易想到了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int X = 1005;
double p[X],dp[X];
int n,k,m;
int main(){
//freopen("sum.in","r",stdin);
int ncase,cnt = 0;
cin>>ncase;
while(ncase--){
cin>>n>>k>>m;
for(int i=0;i<n;i++)
cin>>p[i];
dp[0] = 0;
dp[1] = p[0];
for(int i=2;i<=m;i++){
dp[i] = p[0];
for(int j=1;j<n;j++)
dp[i] += p[j]*pow(dp[i-1],j);
}
dp[m] = pow(dp[m],k);
printf("Case #%d: %.7lf\n",++cnt,dp[m]);
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define debug puts("here");
int s1,s2,t1,t2;
double tot;
double area(int w){
int lc = t1+w;
int rc = t2+w;
int uc = s2-w;
int dc = s1-w;
if(lc>=s2)
return tot;
if(rc<=s1)
return 0;
bool okl = (lc>=s1)&&(lc<=s2);
bool okd = (dc>=t1)&&(dc<=t2);
bool okr = (rc>=s1)&&(rc<=s2);
bool oku = (uc>=t1)&&(uc<=t2);
if(okl&&oku)
return tot-(uc-t1)*(s2-lc)*0.5;
if(okl&&okr)
return ((lc-s1)+(rc-s1))*(t2-t1)*0.5;
if(okd&&oku)
return ((t2-uc)+(t2-dc))*(s2-s1)*0.5;
if(okd&&okr)
return (t2-dc)*(rc-s1)*0.5;
return 0;
}
int main(){
freopen("sum.in","r",stdin);
int ncase,cnt = 0;
int w;
cin>>ncase;
while(ncase--){
cin>>t1>>t2>>s1>>s2>>w;
tot = (t2-t1)*(s2-s1);
double ans = area(w)-area(-w);
ans /= tot;
printf("Case #%d: %.8lf\n",++cnt,ans);
}
return 0;
}
Problem M | UVA 11762 | Race to 1 |
题目:给出一个数n,从不大于n的数中随机选择一个素数,若该数是n的因子p的时候,可以转化为n/p的形式,否则n不变,现在给出n,问他能够转化为1的期望次数
分析:这题可以看做一个随机状态转换机,每个不大于n的素数都是一个等概率事件,根据全期望公式,不难发现递推关系如下:
dp[x] = 1+dp[x]*( (sum[x]-p[x])/sum[x] )+∑ (dp[x/y] / sum[x] ),y为x的素数因子。
1表示为已经转移了一次,若随机选出的不是x的素因子,则x不变化,所以有dp[x]*( (sum[x]-p[x])/sum[x] ),若y为x的素因子,转化为∑ (dp[x/y] / sum[x] )
算法的实现:利用记忆化搜索即可实现该算法。先利用略为修改过的筛法,把不大于x的素数的个数统计出来,以及把x的素数因子存在二维数组里(注意到2*3*5*7*11*13*17*19>1000000),所以第二维可以开到9就行了。另外对于每次求过了的dp[x],不用再次求出(对于所有的测试数据都一样~~)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const int X = 1000002;
int use[X];
int sum[X];
int p[X];
int div[X][9],top[X];
int n;
double dp[X];
void init(){
memset(use,0,sizeof(use));
memset(top,0,sizeof(top));
for(int i=2;i<X;i++){
if(use[i])
sum[i] = sum[i-1];
else{
top[i] = 1;
div[i][0] = i;
sum[i] = sum[i-1]+1;
for(int j=i+i;j<X;j+=i){
use[j] = true;
div[j][top[j]++] = i;
}
}
}
}
double f(int x){
if(use[x])
return dp[x];
use[x] = true;
if(x==1)
return dp[x] = 0;
double ans = sum[x];
for(int i=0;i<top[x];i++)
ans += f(x/div[x][i]);
return dp[x] = ans/top[x];
}
int main(){
freopen("sum.in","r",stdin);
init();
int ncase,cnt = 0;
cin>>ncase;
memset(use,false,sizeof(use));
while(ncase--){
cin>>n;
printf("Case %d: %.10lf\n",++cnt,f(n));
}
return 0;
}