【poj2960】 Collecting Bugs
http://poj.org/problem?id=2096 (题目链接)
题意
有一个程序,其中有s个子结构,每个子结构出bug的概率相等。bug总共分成n类,每种bug出现的概率相等。每天找出一个bug,求所有子结构都出现bug并且每种bug都出现过所需要的期望天数。
Solution
终于领会了期望dp的一点点。。。
细节
%.4lf用G++交会Wa。。
代码
// poj2096
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int maxn=1010;
double f[maxn][maxn];
int n,m;
int main() {
scanf("%d%d",&n,&m);
f[n][m]=0;
for (int i=n;i>=0;i--)
for (int j=m;j>=0;j--) {
if (i+1<=n) f[i][j]+=f[i+1][j]*(1-(double)i/n)*(double)j/m;
if (j+1<=m) f[i][j]+=f[i][j+1]*(1-(double)j/m)*(double)i/n;
if (i+1<=n && j+1<=m) f[i][j]+=f[i+1][j+1]*(1-(double)i/n)*(1-(double)j/m);
if (i!=n || j!=m) f[i][j]=(f[i][j]+1)/(1-(double)i/n*(double)j/m);
}
printf("%.4lf",f[0][0]);
return 0;
}
【poj3744】 Scout YYF I
http://poj.org/problem?id=3744 (题目链接)
题意
给出n个雷,分别在 a[1]...a[n] ,走一步概率为 p ,走两步概率为 1-p ,一开始在 1 号位置,问安全到达终点的概率。
Solution
很显然的dp:f[i]=p*f[i-1]+(1-p)*f[i-2]。考虑a[i]位置上有雷,那么安全通过的概率也就是到达f[a[i]+1]的概率为:f[a[i]-1]*(1-p)。
因为a[i]很大,所以要分段用矩阵快速幂。
细节
代码能力下降的厉害。。。莫名Wa了的可以去看看Discuss,好坑。。
代码
// poj3744
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1<<30
#define eps 1e-8
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
double tmp[2][2],f[2][2],t[2][2],p;
int a[20],n;
void power(int k) {
t[0][0]=p,t[0][1]=1,t[1][0]=1-p,t[1][1]=0;
while (k) {
if (k&1) {
for (int i=0;i<=1;i++)
for (int j=0;j<=1;j++) {
tmp[i][j]=0;
for (int k=0;k<=1;k++) tmp[i][j]+=f[i][k]*t[k][j];
}
memcpy(f,tmp,sizeof(f));
}
k>>=1;
for (int i=0;i<=1;i++)
for (int j=0;j<=1;j++) {
tmp[i][j]=0;
for (int k=0;k<=1;k++) tmp[i][j]+=t[i][k]*t[k][j];
}
memcpy(t,tmp,sizeof(t));
}
}
int main() {
while (scanf("%d%lf",&n,&p)!=EOF) {
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);a[0]=0;
f[1][1]=1;
for (int i=1;i<=n;i++) {
f[1][0]=f[1][1]*p;
f[0][1]=f[1][0]*p+f[1][1]*(1-p);
f[0][0]=f[0][1]*p+f[1][0]*(1-p);
if (a[i]-a[i-1]==1) {f[1][1]=0;break;}
else if (a[i]-a[i-1]==2) f[1][1]=f[1][1]*(1-p);
else if (a[i]-a[i-1]==3) f[1][1]=f[1][0]*(1-p);
else if (a[i]-a[i-1]==4) f[1][1]=f[0][1]*(1-p);
else power(a[i]-a[i-1]-5),f[1][1]=(1-p)*f[0][0];
}
if (fabs(f[1][1])<eps) puts("0.0000000");
else printf("%.7lf
",f[1][1]);
}
return 0;
}
【codeforces 148D】 Bag of mice
http://codeforces.com/problemset/problem/148/D (题目链接)
题意
包中有w个白鼠,b个黑鼠。公主和龙轮流画老鼠,公主先画,谁先画到白鼠谁就赢。龙每画完一只老鼠,就会有另一只老鼠从包中跑出来。每只老鼠被画到以及跑出的概率相等,问公主获胜的概率。
Solution
令${f_{0/1,i,j}}$表示此时公主/龙选,包中还剩i只白鼠,j只黑鼠,公主赢的概率。那么转移很显然:
$${f_{0,i,j}=frac{i}{i+j}+frac{j}{i+j}*f_{1,i,j-1}}$$
$${f_{1,i,j}=frac{j}{i+j}*(frac{i}{i+j-1}*f_{0,i-1,j-1}+frac{j}{i+j-1}*f_{0,i,j-2})}$$
细节
一个加号没打,看半天没看出来。。
代码
// codeforces148D
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int maxn=1010;
double f[2][maxn][maxn];
int n,m;
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) f[0][i][0]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) {
f[0][i][j]=(double)(i+j*f[1][i][j-1])/(i+j);
f[1][i][j]=(double)(i*f[0][i-1][j-1])/(i+j-1);
if (j>1) f[1][i][j]+=(double)((j-1)*f[0][i][j-2])/(i+j-1);
f[1][i][j]*=(double)j/(i+j);
}
printf("%.9lf",f[0][n][m]);
return 0;
}
【bzoj1415】 Noi2005—聪聪和可可
http://www.lydsy.com/JudgeOnline/problem.php?id=1415 (题目链接)
题意
一张图,聪聪想吃可可。每单位时间聪聪可以先移动两次;可可后移动一次或停在原地,它们的概率相等。问聪聪吃到可可的期望时间。
Solution
先bfs跑出两点间的最短距离。然后记忆化搜索就可以了,很水。。
代码
// bzoj1415
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int maxn=1010;
struct edge {int to,next;}e[maxn<<1];
int head[maxn],r[maxn],S,T,n,m,cnt;
int dis[maxn][maxn],vis[maxn][maxn];
double f[maxn][maxn];
void link(int u,int v) {
e[++cnt]=(edge){v,head[u]};head[u]=cnt;
e[++cnt]=(edge){u,head[v]};head[v]=cnt;
}
void bfs(int s,int *d) {
queue<int> q;q.push(s);
while (!q.empty()) {
int x=q.front();q.pop();
for (int i=head[x];i;i=e[i].next)
if (!d[e[i].to] && e[i].to!=s) d[e[i].to]=d[x]+1,q.push(e[i].to);
}
}
double dfs(int s,int t) {
if (vis[s][t]) return f[s][t];
if (dis[s][t]==0) return 0;
if (dis[s][t]<=2) return 1;
vis[s][t]=f[s][t]=1;
int k=inf,tmp=s;
for (int i=head[tmp];i;i=e[i].next)
if (dis[t][e[i].to]<dis[tmp][t] && e[i].to<k) k=e[i].to;
tmp=k;k=inf;
for (int i=head[tmp];i;i=e[i].next)
if (dis[t][e[i].to]<dis[tmp][t] && e[i].to<k) k=e[i].to;
tmp=k;
for (int i=head[t];i;i=e[i].next)
f[s][t]+=(double)1/(r[t]+1)*dfs(tmp,e[i].to);
f[s][t]+=(double)1/(r[t]+1)*dfs(tmp,t);
return f[s][t];
}
int main() {
scanf("%d%d",&n,&m);
scanf("%d%d",&S,&T);
for (int u,v,i=1;i<=m;i++) {
scanf("%d%d",&u,&v);
link(u,v);r[u]++;r[v]++;
}
for (int i=1;i<=n;i++) bfs(i,dis[i]);
printf("%.3lf",dfs(S,T));
return 0;
}
【codeforces 442B】 Andrey and Problem
http://codeforces.com/problemset/problem/442/B (题目链接)
题意
n个人,每个人有p[i]的概率出一道题。问如何选择其中s个人使得这些人正好只出1道题的概率最大。
Solution
很显然的概率dp,过了样例即可AC。。话说我为什么要刷B题→_→
代码
// codeforces442B
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int maxn=200;
double f[maxn][maxn],p[maxn][maxn],a[maxn];
int n;
int main() {
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
for (int i=1;i<=n;i++) {
p[i-1][0]=1;
for (int j=1;j<=i;j++) {
if (f[i-1][j]<f[i-1][j-1]*(1-a[i])+p[i-1][j-1]*a[i]) {
f[i][j]=f[i-1][j-1]*(1-a[i])+p[i-1][j-1]*a[i];
p[i][j]=p[i-1][j-1]*(1-a[i]);
}
else f[i][j]=f[i-1][j],p[i][j]=p[i-1][j];
}
}
double ans=0;
for (int i=1;i<=n;i++) ans=max(ans,f[n][i]);
printf("%.12lf",ans);
return 0;
}
【poj3071】 Football
http://poj.org/problem?id=3071 (题目链接)
题意
${2^n}$个队伍打淘汰赛,输的被淘汰。第1个队打第2个队,第3个队打第4个队······给出第i个队伍打赢第j个队伍的概率p[i][j],求哪只队伍获得冠军的可能性最大
Solution
很简单,想到一个dp方程:${f_{i,j}}$表示第i轮,j胜出的概率。转移很显然:
$${f_{i,j}=f_{i-1,j}×f_{i-1,k}×p_{j,k}}$$
代码
// poj3071
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 1<<30
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
int bin[20],n,m;
double f[1000][1000],p[1000][1000];
int main() {
bin[0]=1;for (int i=1;i<=10;i++) bin[i]=bin[i-1]<<1;
while (scanf("%d",&n) && n>0) {
int m=1<<n;
for (int i=1;i<=m;i++)
for (int j=1;j<=m;j++) scanf("%lf",&p[i][j]);
for (int i=1;i<=m;i++) f[0][i]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) {
int s=(j-1)/bin[i-1]+1;
f[i][j]=0;
if (s&1) {
for (int k=s*bin[i-1]+1;k<=(s+1)*bin[i-1];k++)
f[i][j]+=f[i-1][j]*f[i-1][k]*p[j][k];
}
else {
for (int k=(s-2)*bin[i-1]+1;k<=(s-1)*bin[i-1];k++)
f[i][j]+=f[i-1][j]*f[i-1][k]*p[j][k];
}
}
int ans=1;
for (int i=2;i<=m;i++) if (f[n][ans]<f[n][i]) ans=i;
printf("%d
",ans);
}
return 0;
}