很有意思的概率dp问题...因为有相对性在里面,所以转移方法很巧妙。
#include <cmath> #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <vector> #include <algorithm> #include <stack> #include <queue> using namespace std; typedef long long ll; typedef unsigned long long ull; /*【p2059】卡牌游戏 类似约瑟夫游戏,求每个玩家获胜的概率。*/ //【标签】概率dp + 状态设计 + 数学分析(相对性) //f[i][j]表示还剩i个人时、当前相对庄家位置为j的人最终获胜、的概率。f[1][1]=1。 //枚举庄家抽到的牌k,即此时被淘汰的人的相对位置为c。下一局i-1个人,更新庄家。 //即:当c>j时,第j个人是新庄家之后的第i−c+j个人;当c<j时,第j个人-->第j−c个人。 //转移方程:f[i][j]=f[i][j]+f[i−1][i−c+j]÷m,c>j;f[i][j]=f[i][j]+f[i−1][j−c]÷m,c<j。 void reads(int &x){ int fx=1;x=0;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')fx=-1;s=getchar();} while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s-'0');s=getchar();} x=x*fx;//正负号 } int n,m,a[10019]; double f[1019][1019]; int main(){ reads(n),reads(m); for(int i=1;i<=m;i++) reads(a[i]); f[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 c=(a[k]%i==0)?i:(a[k]%i); //牌k,数字c //↑↑即a[k]%i,c为此局被淘汰的人的相对位置 if(c>j) f[i][j]=f[i][j]+f[i-1][i-c+j]/m; if(c<j) f[i][j]=f[i][j]+f[i-1][j-c]/m; } for(int i=1;i<=n;i++) printf("%.2lf%% ",f[n][i]*100.0); } //注意%的符号输出形式:在printf中要写成%% ↑↑
——时间划过风的轨迹,那个少年,还在等你