[JZOJ3168] 【GDOI2013模拟3】踢足球
题目
描述
题目大意
有两个队伍,每个队伍各人。
接到球的某个人会再下一刻随机地传给自己人、敌人和射门,射门有概率会中。
每次射门之后球权在对方号选手。
某个队伍到了分,或者总时间到达时,比赛结束。
询问每种比分的概率。
思考历程
一看就觉得这是一道DP
设一个五维的状态,其中两维表示比分,一维表示时间,一维表示分数,还有一维表示球权。
不得不说这是最粗暴的方法。
接着呢……想了很久就没有去想了,甚至连暴力也没有打。
正解
正解还是DP。
我们试着给DP降维打击,机智的DYP大佬就想到了省去球权的这一维。
因为题目有个重要的性质:当一个球队射门之后,球权必定在对方的号选手。
我们设表示概率,和表示比分,表示时间,后面的表示球权在哪个队(的号选手)。
让我们考虑一下从发球到其中一个队进球这个过程为单位的转移。
我们再设表示某个队发球,某个队进球,花了时间的概率。
于是我们就可以通过来求出了。
接着问题变成了如何求。
还是DP。
设表示从某个队发球,花了时间,球权在的概率。
在DP转移时,如果射门就会转移到,否则还是转移到。
这样题目就基本做完了。
注意一点,在统计答案的时候,对于双方比分都没有到达的情况,我们需要枚举时间,用对应的值乘上在剩下时间中不进球的概率。
显然,这个不进球的概率就是对应时间中值的和。
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 110
#define maxR 11
#define maxT 501
int n,R,T;
double score[N*2],p[N*2];
int e[N*2][N*2];
double h[2][maxT][N*2],g[2][2][maxT],f[maxR][maxR][maxT][2];
double sumh[2][maxT];
int main(){
scanf("%d%d%d",&n,&R,&T);
for (int i=1;i<=n;++i){
scanf("%lf",&score[i]);
int k1,k2;scanf("%d%d",&k1,&k2);
for (int j=1;j<=k1;++j){
int x;scanf("%d",&x);
e[i][x]=1;
}
for (int j=1;j<=k2;++j){
int x;scanf("%d",&x);
e[i][n+x]=1;
}
p[i]=1.0/(k1+k2+1);
}
for (int i=n+1;i<=2*n;++i){
scanf("%lf",&score[i]);
int k1,k2;scanf("%d%d",&k1,&k2);
for (int j=1;j<=k1;++j){
int x;scanf("%d",&x);
e[i][n+x]=1;
}
for (int j=1;j<=k2;++j){
int x;scanf("%d",&x);
e[i][x]=1;
}
p[i]=1.0/(k1+k2+1);
}
for (int st=0;st<=1;++st){
h[st][0][st*n+1]=1;
for (int j=0;j<T;++j)
for (int k=1;k<=2*n;++k){
if (k<=n){
g[st][0][j+1]+=h[st][j][k]*p[k]*score[k];
h[st][j+1][n+1]+=h[st][j][k]*p[k]*(1-score[k]);
}
else{
g[st][1][j+1]+=h[st][j][k]*p[k]*score[k];
h[st][j+1][1]+=h[st][j][k]*p[k]*(1-score[k]);
}
for (int l=1;l<=2*n;++l)
if (e[k][l])
h[st][j+1][l]+=h[st][j][k]*p[k];
}
for (int j=0;j<=T;++j)
for (int k=1;k<=2*n;++k)
sumh[st][j]+=h[st][j][k];
}
f[0][0][0][0]=1;
for (int i=0;i<R;++i)
for (int j=0;j<R;++j)
for (int k=0;k<T;++k)
for (int t=1;k+t<=T;++t){
f[i][j+1][k+t][0]+=f[i][j][k][0]*g[0][1][t]+f[i][j][k][1]*g[1][1][t];
f[i+1][j][k+t][1]+=f[i][j][k][0]*g[0][0][t]+f[i][j][k][1]*g[1][0][t];
}
for (int i=0;i<=R;++i)
for (int j=0;j<=R;++j){
double ans=0;
if (i<R && j<R){
for (int k=0;k<=T;++k)
ans+=f[i][j][k][0]*sumh[0][T-k]+f[i][j][k][1]*sumh[1][T-k];
}
else{
if (i==R && j==R)
break;
for (int k=0;k<=T;++k)
ans+=f[i][j][k][0]+f[i][j][k][1];
}
printf("%.8lf\n",ans);
}
return 0;
}
总结
有时候一个DP不能解决,就用两个DP,如果两个DP不能解决,那就三个……