首页 > 技术文章 > [JLOI2013]卡牌游戏

captain1 2018-11-01 22:01 原文

传送门

这个题一开始不会转移了……因为自己状态设定的不对。

应该参考一下约瑟夫问题的操作,设dp[i][j]表示在有i个人的时候从庄家开始数第j个人的获胜概率。这样的话,我们只要枚举每张卡牌,这样的话,每个人获胜的概率就能由有i-1个人的时候推出来,因为其实淘汰一个人就是相当于把队列向前移动几位,但是胜利者并不会改变。

抽到每张卡的概率相同,所以我们就能得到DP方程:

dp[i][j] += (dp[i-1][(j-c[k] + i)%i]) / m.

这样直接递推一下就可以啦,代码很短。注意最后答案要先乘以100.

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 10005;
const ll INF = 1000000009;
const int mod = 9397;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

int a[M],n,m;
double dp[1005][1005];

int main()
{
   n = read(),m = read();
   rep(i,1,m) a[i] = read();
   dp[1][1] = 1.0;
   rep(i,2,n)
   rep(j,1,i)
   rep(k,1,m)
   {
      int c = (a[k] % i) ? a[k] % i : i;
      if(c > j) dp[i][j] += dp[i-1][i-c+j] / (double)m * 1.0;
      else if(c < j) dp[i][j] += dp[i-1][j-c] / (double)m * 1.0;
   }
   rep(i,1,n) printf("%.2lf%% ",dp[n][i] * 100.0);enter;
   return 0;
}

 

推荐阅读