《中山大學內部選拔(一)》 遊戲

細雨欣然發表於2017-03-14

【問題描述】

  A,B兩人各有不同分值的 M 張牌,他們進行N回合的遊戲,每個回合兩人分別在自己的M張牌裡抽取1張,然後記下各自的分數並把牌收回到自己的牌堆中,然後繼續下一回合。遊戲結束後,兩人的得分就是各自每回合的分值和,如果其中一個人的分數和較大,則這個人就贏得比賽。

  現在告訴你A,B兩人各自M張牌的分值以及遊戲的回數,請你計算A贏得比賽的概率。

【輸入格式】

  首先是T,表示資料組數。
  每組資料包含4行:第1行為M,第2行為A的M張牌的分值,第3行是B的M張牌的分數。第4行為N。

【輸出格式】

  輸出一個保留6位小數的實數,表示A贏得比賽的概率。

【輸入樣例】

1
10
5 9 6 12 13 15 52 11 11 11
8 9 6 3 2 1 85 9 2 3
15

【輸出樣例】

0.620092

【資料範圍】

1<=M<=100 0<=N<=50 每張牌的分值不超過50。

【來源】

《中山大學內部選拔(一)》

這道題我拿到的題資料範圍有誤害的我好一陣亂優化,結果其實直接就能過。
很明顯這是一道數學期望,我們可以直接用遞推得到2個人分別得到每個分數的概率,這時我們要用一個字首和的思想,把乙的分數的字首和算出來,用g(i)表示乙得到小於等於i的分數的概率。然後沒舉每個i用甲得到分數i的概率乘上g(i-1),全部加起來就是答案了。

詳細程式碼如下:(有點醜,畢竟亂優化了一波)

#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1000005;

double d[maxn],q[maxn],g[maxn],p,a1[maxn],b1[maxn];
int a[maxn],b[maxn],cnt1,cnt2;
int m,n;

int main()
{
    freopen("Game.in","r",stdin);
    freopen("Game.out","w",stdout);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        cnt1=cnt2=1;
        scanf("%d",&m);
        for(int i=1;i<=m;i++) scanf("%d",&a[i]);
        sort(a+1,a+1+m);
        for(int i=1;i<=m;i++) scanf("%d",&b[i]);
        sort(b+1,b+1+m);
        p=1.0/(double)m;
        scanf("%d",&n);
        memset(d,0,sizeof(d));
        memset(a1,0,sizeof(a1));
        memset(b1,0,sizeof(b1));
        for(int i=2;i<=m;i++)
        {
            if(a[i]!=a[i-1]) a[++cnt1]=a[i];
            else a1[cnt1]+=p;
        }
        for(int i=1;i<=cnt1;i++) a1[i]+=p,d[a[i]]=a1[i];

        for(int i=2;i<=n;i++)
        for(int j=a[cnt1]*i;j>=a[1]*(i-1);j--)//算甲前i次得到j分的概率
        {
            double ans=0;
            for(int k=1;k<=cnt1;k++) 
            {
                if(j-a[k]<a[1]*(i-1)) break;
                ans+=d[j-a[k]]*a1[k];
            }
            d[j]=ans;
        }

        memset(q,0,sizeof(q));
        for(int i=2;i<=m;i++)
        {
            if(b[i]!=b[i-1]) b[++cnt2]=b[i];
            else b1[cnt2]+=p;
        }
        for(int i=1;i<=cnt2;i++) b1[i]+=p,q[b[i]]=b1[i];

        for(int i=2;i<=n;i++)
        for(int j=b[cnt2]*i;j>=b[1]*(i-1);j--)算乙前i次得到j分的概率
        {
            double ans=0;
            for(int k=1;k<=cnt2;k++) 
            {
                if(j-b[k]<b[1]*(i-1)) break;
                ans+=q[j-b[k]]*b1[k];
            }
            q[j]=ans;
        }
        memset(g,0,sizeof(g));
        int ans2=0;
        int t=max(b[cnt2],a[cnt1])*n;
        for(int i=b[1]*n;i<=t;i++)//算字首和g,主要範圍
        g[i]=g[i-1]+q[i];
        double ans=0;
        for(int i=a[1]*n;i<=t;i++)//求答案
        ans+=g[i-1]*d[i];
        printf("%.6lf\n",ans);
    }
    return 0;
}

相關文章