题解 P2059 【[JLOI2013]卡牌游戏】
【题目描述】
4 4
3 4 5 6
- 输出样例
1
25.00% 25.00% 25.00% 25.00%
- 输入样例
2
5 5
2 3 5 7 11
- 输出样例
2
22.72% 17.12% 15.36% 25.44% 19.36%
【数据范围】
对于
对于
对于
思路:这道题目很明显可以通过枚举来确定最后的胜利者是谁。然后我们就可以根据所有的结果来分析每一个人的胜率了。下面我将举出一个
2 3
1 2 3
第
第
第
根据上面的结果,我们发现:
那么我们怎样才能够进一步地优化我们的程序呢?
其实我们可以使用一种叫做概率动态规划的算法,即我们常说的概率DP。
我们可以用概率DP来解决这个问题。我们可以用
首先我们可以确定当还有
我们可以首先枚举庄家抽到的卡牌
k ,得到这一轮被淘汰的人的位置c 。当然,如果c=j ,就不要考虑了(因为这表示此轮第j 个人被淘汰)。而第
c 个人被淘汰之后,剩下的i-1 个人要组成一个新的环,庄家为第c 个人的下一个。容易算出,当c>j 时,第j 个人是新的环里从新庄家数起的第i-c+j 个人,当c<j 时,第j 个人是新的环里从新庄家数起的第j-c 个人。
上面的引用部分里的内容摘自@xyz32768的博客,放入题解时有删改。
因此我们可以得出状态转移方程:
当
否则当
其中,
下面上
#include <cstdio>
double f[1001][1001];
int a[10001];
int main()
{
int n=0,m=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d",&a[i]);
}
f[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 c=(a[k]%i!=0)?(a[k]%i):i;
if(c>j)
{
f[i][j]+=f[i-1][i-c+j]/m;
}
else if(c<j)
{
f[i][j]+=f[i-1][j-c]/m;
}
}
}
}
for(int i=1;i<=n;i++)
{
printf("%.2lf%% ",f[n][i]*100.0);
}
return 0;
}