給定 n 個數的排列,進行 k 次操作:每次等概率隨機選擇一個區間 [l, r] 並翻轉。求 k 次操作後逆序對數量的期望值。n ≤ 100, k ≤ 100。
定義 dp[i][j] 表示第 i 個元素在第 j 個元素之前的概率(i < j)。初始狀態:按原排列設置 dp[i][j] = 1 或 0。每次翻轉區間 [x, y] 會交換區間內元素相對位置,導致概率轉移。
對於任意元素對 (i, j),翻轉區間 [x, y] 後其新位置 (a, b) 為:
若區間包含 i, j 兩者,則相對順序翻轉,概率變為 1 - dp[i][j];否則保持不變。遍歷所有可能區間,按概率 1/(n(n+1)/2) 累加轉移。
const int N = 105;
double dp[N][N], tmp[N][N];
int a[N];
int main() {
int n, m;
while (cin >> n >> m) {
for (int i = 1; i <= n; ++i) cin >> a[i];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
dp[i][j] = 1.0; // 初始有序對概率為 1
double P = 2.0 / (n * (n + 1)); // 區間選中概率
while (m--) {
memcpy(tmp, dp, sizeof(dp));
memset(dp, 0, sizeof(dp));
for (int x = 1; x <= n; ++x)
for (int y = x; y <= n; ++y) {
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j) {
int u = i, v = j;
if (x <= u && u <= y) u = x + y - u;
if (x <= v && v <= y) v = x + y - v;
if (u > v) swap(u, v);
if (x <= i && j <= y) // 區間包含 i,j 完整翻轉
dp[u][v] += (1 - tmp[i][j]) * P;
else
dp[u][v] += tmp[i][j] * P;
}
}
}
double ans = 0;
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j)
ans += (a[i] > a[j]) ? dp[i][j] : (1 - dp[i][j]);
printf("%.10f\n", ans);
}
return 0;
}
此題展示了概率 DP 在排列組合期望問題中的強大建模能力,狀態設計巧妙避免了直接模擬排列的指數狀態空間。