dp+数学计算
题目链接
题意
已知一个整数n∈[2, 3000]
对于循环操作“将第一个人移至队伍末尾或者移出队伍(概率皆为1/2)”直到剩下最后一人
求每个位置i(1 ≤ i ≤ n)的人成为最后剩下的人的概率是多少
思考
只有一个人,第一个人留下的概率是1
两个人的时候,
始:1 2
1/2:2 或 2 1
1/4: 1 或 1 2
……
第一个人留下概率:1/21+1/23+1/25+... ≈ 0.66666
第二个人留下概率:1/22+1/24+1/26+... ≈ 0.33333
则答案为1: 2,即 1/3 和 2/3
但当人数n更大的时候就无法快速计算答案且不好代码实现,所以考虑通过dp的方式解决问题
题解
用dp方法求解,建立n^2的二维数组,其中 dp[i] [j]表示剩i个人的时候第j位置的人最后留下的概率 (j <= i)
dp[1][1] = 1 dp[i][1] = 1 / 2 * dp[i][i] 第一个人剩下的概率是第一个人不被移走而被移到最后一个的位置的时候 最后一个位置成功的概率 dp[i][j] = 1 / 2 * dp[i][j - 1] 当第j的前一个位置被移到最后一位 j就变成了j-1的位置 + 1 / 2 * dp[i - 1][j - 1] 当j的前一个位置被移走 j变成了j-1的位置 总共剩下i-1个数字
则有假设剩i个人的时候 每个位置的成功概率:
设dp[i] [i] 为 x; a, b...为常数,是上一层已经求出来的答案
1 2 3 ... i
则
ac代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int maxn = 1e6 + 10;
const int mod1 = 1e9 + 7;
const int mod2 = 998244353;
string Y = "Yes\n";
string N = "No\n";
int ksm(int a, int b, int mod) {
int ans = 1;
while (b) {
if (b & 1) {
ans = ans * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return ans;
}
int dp[3010][3010], box[3010];
void solve() {
int n;
cin >> n;
box[0] = 1;
for (int i = 1; i <= n; i++) {
box[i] = box[i - 1] * 2 % mod2;
}
dp[1][1] = 1;
for (int i = 2; i <= n; i++) {
int x = 0;
for (int j = 1; j <= i - 1; j++) {
x = x + box[j] * dp[i - 1][j] % mod2;
x %= mod2;
}
int y = (box[i] - 1 + mod2) % mod2;
x = x * ksm(y, mod2 - 2, mod2) % mod2; // dp[i][i]
dp[i][i] = x, dp[i][1] = x * ksm(2, mod2 - 2, mod2) % mod2;
for (int j = 2; j <= i - 1; j++) {
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j - 1]) % mod2 * ksm(2, mod2 - 2, mod2) % mod2;
dp[i][j] %= mod2;
}
}
for (int i = 1; i <= n; i++) {
cout << dp[n][i] << " ";
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie();
cout.tie();
int yq = 1;
// cin >> yq;
while (yq--) {
solve();
}
return 0;
}