水题乱刷
【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\n",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\n",ans); } return 0; }