[JLOI2013]卡牌游戏 题解

[JLOI2013]卡牌游戏 题解

题目地址:洛谷:【P2059】[JLOI2013]卡牌游戏 – 洛谷、BZOJ:Problem 3191. — [JLOI2013]卡牌游戏

题目描述

N个人坐成一圈玩游戏。一开始我们把所有玩家按顺时针从1到N编号。首先第一回合是玩家1作为庄家。每个回合庄家都会随机(即按相等的概率)从卡牌堆里选择一张卡片,假设卡片上的数字为X,则庄家首先把卡片上的数字向所有玩家展示,然后按顺时针从庄家位置数第X个人将被处决即退出游戏。然后卡片将会被放回卡牌堆里并重新洗牌。被处决的人按顺时针的下一个人将会作为下一轮的庄家。那么经过N-1轮后最后只会剩下一个人,即为本次游戏的胜者。现在你预先知道了总共有M张卡片,也知道每张卡片上的数字。现在你需要确定每个玩家胜出的概率。
这里有一个简单的例子:
例如一共有4个玩家,有四张卡片分别写着3,4,5,6.
第一回合,庄家是玩家1,假设他选择了一张写着数字5的卡片。那么按顺时针数1,2,3,4,1,最后玩家1被踢出游戏。
第二回合,庄家就是玩家1的下一个人,即玩家2.假设玩家2这次选择了一张数字6,那么2,3,4,2,3,4,玩家4被踢出游戏。
第三回合,玩家2再一次成为庄家。如果这一次玩家2再次选了6,则玩家3被踢出游戏,最后的胜者就是玩家2.

输入输出格式

输入格式:
第一行包括两个整数N,M分别表示玩家个数和卡牌总数。
接下来一行是包含M个整数,分别给出每张卡片上写的数字。

输出格式:
输出一行包含N个百分比形式给出的实数,四舍五入到两位小数。分别给出从玩家1到玩家N的胜出概率,每个概率之间用空格隔开,最后不要有空格。

输入输出样例

输入样例#1:

5 5
2 3 5 7 11

输出样例#1:

22.72% 17.12% 15.36% 25.44% 19.36%

输入样例#2:

4 4
3 4 5 6

输出样例#2:

25.00% 25.00% 25.00% 25.00%

说明

对于30%的数据,有1<=N<=10
对于50%的数据,有1<=N<=30
对于100%的数据,有1<=N<=50 1<=M<=50 1<=每张卡片上的数字<=50

题解

10%自然就是爆搜乱搞了。50%实话说没想到。
看上去算法是[eq]O(n^3)[/eq]的。我们需要注意到一点,即淘汰的操作仅关心被淘汰人与庄家的相对位置关系,记录这个相对位置关系就好办了。且这个游戏每次只淘汰一人,即总是从剩i人向剩i-1人发展。设计状态dp[i][j]表示剩下i人从庄家开始顺时针数j人这个人胜出的概率,我们考虑随机从当前局面淘汰一人,只需要知道淘汰并且重新选定庄家后现在这个人在下一个局面的什么位置即可,即转移是
\displaystyle dp[i][j] = \sum \frac{dp[i-1][j']}{m}
还剩一人时这个人就胜出了,因此dp[1][1]=1。由于初始的时候1做庄家,答案就是dp[n][1]~dp[n][n]。

代码

// Code by KSkun, 2018/4
#include <cstdio>
#include <cctype>
#include <cstring>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 55;

int n, m, a[MAXN];
double dp[MAXN][MAXN];

int main() {
    n = readint(); m = readint();
    for(int i = 1; i <= m; i++) {
        a[i] = readint();
    }
    dp[1][1] = 1;
    for(int i = 2; i <= n; i++) {
        for(int j = 1; j <= i; j++) {
            for(int k = 1; k <= m; k++) {
                int nxt = a[k] % i;
                if(!nxt) nxt = i;
                if(nxt > j) dp[i][j] += dp[i - 1][i - nxt + j] / m;
                else if(nxt < j) dp[i][j] += dp[i - 1][j - nxt] / m;
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        printf("%.2lf%% ", dp[n][i] * 100);
    }
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据