[Luogu P2059] [BZOJ 3191] [JLOI2013]卡牌游戏

洛谷传送门
BZOJ传送门

题目描述

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

这里有一个简单的例子:

例如一共有 4 4 4个玩家,有四张卡片分别写着 3 , 4 , 5 , 6. 3,4,5,6. 3,4,5,6.

第一回合,庄家是玩家 1 1 1,假设他选择了一张写着数字 5 5 5的卡片。那么按顺时针数 1 , 2 , 3 , 4 , 1 1,2,3,4,1 1,2,3,4,1,最后玩家 1 1 1被踢出游戏。

第二回合,庄家就是玩家 1 1 1的下一个人,即玩家 2 2 2.假设玩家 2 2 2这次选择了一张数字 6 6 6,那么 2 , 3 , 4 , 2 , 3 , 4 2,3,4,2,3,4 2,3,4,2,3,4,玩家 4 4 4被踢出游戏。

第三回合,玩家 2 2 2再一次成为庄家。如果这一次玩家 2 2 2再次选了 6 6 6,则玩家 3 3 3被踢出游戏,最后的胜者就是玩家 2 2 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 % 30\% 30%的数据,有 1 ≤ N ≤ 10 1\le N\le 10 1N10

对于 50 % 50\% 50%的数据,有 1 ≤ N ≤ 30 1\le N\le 30 1N30

对于 100 % 100\% 100%的数据,有 1 ≤ N ≤ 50   1 ≤ M ≤ 50 1\le N\le 50\ 1\le M\le 50 1N50 1M50 $1\le 每 张 卡 片 上 的 数 字 每张卡片上的数字 \le 50$

解题分析

如果完整记录剩 i i i个人的时候剩下的人的编号, 显然要 M L E + T L E MLE+TLE MLE+TLE

我们就只记录剩下 i i i个人的时候第 i i i个人的胜率, 倒推回有 n n n个人的时候就行了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cmath>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define db double
#define MX 55
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
db dp[MX][MX];
int to[MX];
int n, m, tar;
int main(void)
{

    in(n), in(m);
    for (R int i = 1; i <= m; ++i) in(to[i]);
    for (R int i = 1; i <= n; ++i) dp[1][i] = 1;
    for (R int i = 2; i <= n; ++i)
    for (R int j = 1; j <= i; ++j)
    for (R int k = 1; k <= m; ++k)
    {
        tar = (to[k] - 1) % i + 1;
        if(tar > j) dp[i][j] += dp[i - 1][j - tar + i] / m;
        else if(tar < j) dp[i][j] += dp[i - 1][j - tar] / m;
        //tar=j的时候j被淘汰了, 就不加上了
    }
    for (R int i = 1; i <= n; ++i) printf("%.2lf%% ", dp[n][i] * 100);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值