Code Jam--Good Luck

本文介绍了一道关于随机数的编程挑战题目,通过贝叶斯算法和最大后验概率的方法来解决该问题。文章详细解释了如何利用DFS搜索可能的数字组合,并通过哈希表来提高查找效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:每次从[2,M]中随机选一个数写在卡牌上,重复N次;再从N个卡牌中任意选择x(0<=x<=N)个,每个卡牌被选的概率为1/2,计算卡牌上数的乘积;重复K次,则我们有K个乘积;现给出K个乘积,请猜测卡牌上的数。

原题:https://code.google.com/codejam/contest/2418487/dashboard#s=p2;

官方解答:https://code.google.com/codejam/contest/2418487/dashboard#s=a&a=2


首先做了14年round1的最后一道随机题,感觉比较有趣,就做了一下这道13年的随机题,有点难啊。

题解:如官方解答,这种题看似随机,其实都有其固定解法,当然解法可能不唯一。

一、贝叶斯

这种题一般都会选择贝叶斯算法,或者最大后验概率。假设一种组合为A,一组乘积为P,则最可能的组合A’ = max{Proba(Ai|P),Ai},而Proba(Ai|P) = Proba(P|Ai)*Proba(Ai)/Proba(P)。对于不同Ai,Proba(P)都是一样的,但是分子的Proba(P|Ai)和Proba(Ai)是不一样的,我们就是要计算这个。

二、概率计算

  1. 计算Proba(Ai),要先知道所有的Ai,对于乘积结果来说,Ai中数字的顺序是无关紧要的,可以用DFS搜索可能的Ai并保存,搜索过程中保持数字递增,相同数字连续。
  2. 卡牌中乘积结果的条件概率则可以直接以二进制位代表N张卡牌的选择,枚举这2^N中等概率的选择。
  3. 对于每组乘积,计算出每种组合产生这组乘积的概率,选出最大概率的组合。

注意,为了能直接根据乘积找出它的条件概率,又因为乘积最大为8^12,可以采用hash散列处理。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define maxP 4157

vector<vector<int> > cards;
vector<int> tmpCards;
vector<double> cardsProba;          //每种卡牌出现的概率
int fact[15];
int cardsNum;
int R,N,M,K;

struct node
{
    long long product;
    int proba;
    node* next;
}*** conditionProba;        //每种乘积对应卡牌上的数出现的概率

int generateCards(int beginPos,int digit,double tmpProba)
{
    if(beginPos == N)
        //卡牌已写完
    {
        cards.push_back(tmpCards);
        cardsNum++;
        cardsProba.push_back(tmpProba);
        return 0;
    }
    if(digit == M+1)
    //卡牌没写完,但是已经不能在写其他数
        return 0;
    for(int i = beginPos;i < N;i++)
    {
        for(int j = digit;j <= M;j++)
        //卡牌上的数要递增地写,防止冗余
        {
            for(int k = beginPos;k <= i;k++)
                //将beginPos到i的卡牌写上j
            {
               tmpCards[k] = j;
            }
            generateCards(i+1,j+1,tmpProba/fact[i-beginPos+1]);
        }
    }
    return 0;
}

//产生阶乘计算概率
int generateFact()
{
    fact[1] = 1;
    for(int i = 2;i <= N;i++)
    {
        fact[i] = fact[i-1]*i;
    }
    return 0;
}

node* hashCheck(node* in,long long product)
{
    node* tmpNode;
    for(tmpNode = in;tmpNode != NULL;tmpNode = tmpNode->next)
    {
        if(tmpNode->product == product)
        {
            break;
        }
    }
    return tmpNode;
}

int hashInsert(node** in,long long product)
{
    int slot = product%maxP;
    node* tmpNode = hashCheck(in[slot],product);
    if(tmpNode == NULL)
        //如果没有product,则新生成节点加入链表
    {
        tmpNode = new node;
        tmpNode->product = product;
        tmpNode->proba = 1;
        tmpNode->next = in[slot];
        in[slot] = tmpNode;
    }
    else
    {
        (tmpNode->proba)++;
    }
    return 0;
}

int generateConditionProba()
{
    long long product;
    int maxDigit = (1<<N)-1;
    conditionProba = new node**[cardsNum];
    int i,j,k;
    for(k = 0;k < cardsNum;k++)
    {
        conditionProba[k] = new node*[maxP];
        memset(conditionProba[k],0,sizeof(node*)*(maxP));
        for(i = 0;i <= maxDigit;i++)
            //枚举2^N种卡牌乘法
        {
            product = 1;
            for(j = 0;j < N;j++)
            {
                if(i&(1<<j))
                {
                    product *= (long long)cards[k][j];
                }
            }
            hashInsert(conditionProba[k],product);
        }
    }
    return 0;
}

int main()
{
    int T;
    long long product[15];
    int i,j,t;
    double maxProba;
    double tmpProba;
    node* tmpNode;
    int maxProbaIndex;
    scanf("%d",&T);
    for(t = 1;t <= T;t++)
    {
        scanf("%d%d%d%d",&R,&N,&M,&K);
        cardsNum = 0;
        tmpCards.resize(N);
        generateFact();
        generateCards(0,2,fact[N]);
        generateConditionProba();
        printf("Case #%d:\n",t);
        while(R--)
        {
            maxProba = -1;
            for(i = 0;i < K;i++)
            {
                scanf("%lld",product+i);
            }
            for(i = 0;i < cardsNum;i++)
            {
                tmpProba = cardsProba[i];
                for(j = 0;j < K;j++)
                {
                    tmpNode = hashCheck(conditionProba[i][product[j]%maxP],product[j]);
                    if(tmpNode == NULL)
                    {
                        tmpProba = 0;
                        break;
                    }
                    else
                    {
                        tmpProba *= ((double)tmpNode->proba);
                    }
                }
                if(maxProba < tmpProba)
                {
                    maxProba = tmpProba;
                    maxProbaIndex = i;
                }
            }
            for(i = 0;i < N;i++)
            {
                printf("%d",cards[maxProbaIndex][i]);
            }
            printf("\n");
        }
        delete[] conditionProba;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值