P2059 [JLOI2013] 卡牌游戏

题意

$n$ 个人坐成一圈,按顺时针从 $1$ 到 $n$ 编号。第一回合是玩家 $1$ 作为庄家。每个回合随机从卡牌堆里选择一张卡片,设卡片上的数字为 $val_k$,将按顺时针从庄家位置数第 $val_k$ 个人处决。被处决的人按顺时针的下一个人将会作为下一轮的庄家。求每个玩家胜出的概率。

Solution

显然概率 dp。发现每个人的编号是不重要的,只有相对位置是有意义的信息。故设 $dp_{i,j}$ 为当前有 $i$ 个人,庄家为 $j$ 的获胜概率,枚举每一张卡牌 $val_k$,则有转移方程:$$ \begin{cases}\, dp_{i,j}\gets \dfrac{dp_{i-1,j-val_k}}{m},&val_k\lt j\\\,dp_{i,j} \gets 0,&val_k=j\\\,dp_{i,j}\gets \dfrac{dp_{i-1,j+i-val_k}}{m},& val_k\gt j \end{cases} $$ 初始状态为 $dp_{1,1}=100\%$。时间复杂度为 $\mathcal{O}(n^2\times m)$,可以通过本题。

code

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int res=0,flag=1;
    char ch=getchar();
    while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
    while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
    return res*flag;
}
int val[100];
double dp[100][100];
int main(int argc,const char *argv[])
{
    int n=read(),m=read();
    for(int i=1;i<=m;i++)
        val[i]=read();
    sort(val+1,val+m+1);
    dp[1][1]=1.0;
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<=i;j++)
        {
            for(int k=1;k<=m;k++)
            {
                int pos=val[k]%i;
                if(pos==0)
                    pos=i;
                if(pos<j)
                    dp[i][j]+=dp[i-1][j-pos]/m;
                if(pos==j)
                    dp[i][j]+=0.0;
                if(pos>j)
                    dp[i][j]+=dp[i-1][j+i-pos]/m;
            }
        } 
    }
    for(int i=1;i<=n;i++)
        printf("%.2f%% ",dp[n][i]*100.0);
    return 0;
}
posted @ 2023-07-31 14:08  Che_001  阅读(11)  评论(0)    收藏  举报  来源