GDOI2016模拟8.10踢足球

题目
2n 个人分成2 队玩足球。每队每个球员的队服上有一个1 到n 的正整数(同一个队内的数字不重复)。我们可以知道每个球员的精准度,他可以进行传球的队友集合F 和可以把他的球抢走的敌队球员集合E。当一个球员接到了球,在恰好一秒钟后会发生以下的事件:
1) 该球员把球传给F 集合中的随意一个队员。
2) E 集合中的随意一个敌队球员把球抢走。
3) 该球员射球。
如果该球员射球,他能得分的概率等于他的精准度。在射球之后,不管能否得分,球都会判给敌队的一号球员。发生以上三种不同事件的概率比是|F| : |E| : 1。概率比取决于当前接到球的球员(|S|代表S 集合的规模),而与之前发生的任何事件无关。“随意”的意思是所有在集合F(或E)的球员有相同的概率可以从目前带球的球员得到球(或抢走球)。球在球员之间转移的时间忽略不计。
比赛从1 队的1 号球员带球开始,直到任意一队得到R 分或已经过了T 秒。对于任何可能的最终比分,计算出得出该比分的概率。

会暴力dp的人会发现,暴力会超时,
这题我们可以无视掉球在球员间的传递情况(这个可以预处理),用fi,ti,A,B表示i队开球,到了ti时刻,比分为A:B的概率

这个我们可以通过两个函数得到转移:设gi,j,ti为i队开球,到了ti时刻j进球的概率,delayi,j,ti为i队开球,到了ti时刻,j在控球。(当用delay转移的时候直接把它推到最后时刻,不然会和g转移的有重复)

对于g和delay,我们可以通过函数h获得:设hi,j,k,ti为i队开球,到了ti时刻,球在j队k球员身上。

对于球员间的转移,我们可以预处理出pi1,j1,i2,j2为i1队j1球员传球i2队j2球员的概率。(注意,断球视作传球,没射进看做向对方守门员传球)

进球的概率用shooti1,j1表示。

具体视代码,当然自己想出来就更好了。

贴代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 111
#define T 511
#define R 12
#define EPS 0.0000001
using namespace std;
int n,r,t,nn;
double shoot[2][N],p[2][N][2][N],f[2][T][R][R],g[2][2][T],h[2][2][N][T],ans[R][R],delay[2][T];
void init(){
    static int x,y,z;
    static double rate,P; 
    scanf("%d %d %d",&n,&r,&t);
    for (int i=0;i<=1;i++)
        for (int j=0;j<n;j++){
            cin>>rate>>x>>y;
            P=(double)1/(x+y+1);
            shoot[i][j]=P*rate;
            for (int k=1;k<=x;k++)
                scanf("%d",&z),p[i][j][i][z-1]=P;
            for (int k=1;k<=y;k++)
                scanf("%d",&z),p[i][j][!i][z-1]=P;
            p[i][j][!i][0]+=P*(1-rate);
        }
}
void work(){
    h[0][0][0][0]=h[1][1][0][0]=1;
    for (int ti=0;ti<t;ti++)
        for (int i=0;i<=1;i++)
            for (int j=0;j<=1;j++)
                for (int k=0;k<n;k++)
                    if (h[i][j][k][ti])
                    for (int pp=0;pp<=1;pp++)
                        for (int x=0;x<n;x++)
                            h[i][pp][x][ti+1]+=h[i][j][k][ti]*p[j][k][pp][x];
    for (int ti=0;ti<t;ti++)
        for (int i=0;i<=1;i++)
            for (int j=0;j<=1;j++)
                for (int k=0;k<n;k++)
                    g[i][j][ti+1]+=h[i][j][k][ti]*shoot[j][k],delay[i][ti+1]+=h[i][j][k][ti+1];
    f[0][0][0][0]=1;
    for (int ti=0;ti<t;ti++)
        for (int i=0;i<=1;i++)
            for (int k=0;k<r;k++)
                for (int l=0;l<r;l++){
                    f[i][t][k][l]+=f[i][ti][k][l]*delay[i][t-ti];
                    for (int x=1;x<=t-ti;x++)
                        for (int j=0;j<=1;j++)
                            f[!j][ti+x][k+!j][l+j]+=f[i][ti][k][l]*g[i][j][x];
                }
    for (int ti=0;ti<t;ti++)
        for (int i=0;i<=1;i++)
            for (int j=0;j<r;j++)
                ans[j][r]+=f[i][ti][j][r],ans[r][j]+=f[i][ti][r][j];
    for (int i=0;i<=1;i++)
        for (int j=0;j<=r;j++)
            for (int k=0;k<=r;k++)
                ans[j][k]+=f[i][t][j][k];
}
void write(){
    for (int i=0;i<=r;i++)
        for (int j=0;j<=r;j++)
            if (ans[i][j]>0)
                printf("%.10f\n",ans[i][j]);
}
int main(){
    init();
    work();
    write();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值