題目獻上(LYP君原創!)
1 幣 (coin)
1.1 題目描述
有一排硬幣堆,兩個人輪流取硬幣。每個選手隨機取最左邊或者最右邊的一堆硬幣。求先手期望取得的硬幣數。
1.2 輸入格式
本題有多組測試數據。
第一行一個數 T ,表示數據組數。
對於每組測試數據,第一行一個正整數 n ,表示有多少堆硬幣。
第二行 n 個非負整數,依次表示每一堆硬幣的個數。
1.3 輸出格式
對於每組測試數據,輸出一行一個數,表示先手期望取得的硬幣數。保留 3 位小數。
1.4 樣例輸入
2
3
1 4 9
4
5 5 5 5
1.5 樣例輸出
9.500
10.000
1.6 樣例解釋
對於第一個測試數據:
先手第一次取 後手第一次取 先手第二次取 先手取得的石子數 出現的概率
1 4 9 10 0.25
1 9 4 5 0.25
9 1 4 13 0.25
9 4 1 10 0.25
所以先手期望取得的硬幣數爲:
10 × 0.25 + 5 × 0.25 + 13 × 0.25 + 10 × 0.25 = 9.5
對於第二個測試數據:
由於兩個人都會取 2 堆石子,且每堆石子都是 5 個,所以無論怎麼取答案都是 10 。
我能夠想到的就是概率與每堆個數無關
然後用補集的思想解
但是因爲我比較弱,所以沒有推出正確並且優的狀態和轉移,然後記憶化的寫法真心不熟,醜又WA
不廢話了寫正解
f[i][j]表示從連續的堆數爲i的取此連續的堆中的第j堆的概率
假如f[i][j]是先手成功的概率,而g[i][j]是後手成功的概率
f[i][j] + g[i][j] = 1,顯然的啵
cuizimu大牛點醒我的
易知f[i][j] 可以往兩個狀態轉
然後再經過轉換之後變爲
於是此題解法爲預處理出概率,再乘上數即可
然後是LYP的code,不過我將冗雜的代碼刪去了些無謂的宏定義
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
const int limt = 1000 + 5;
int t, n, m;
double tot, f[limt][limt];
int main()
{
freopen("coin.in", "r", stdin);
freopen("coin.out", "w", stdout);
for (int i = 1; i <= limt; ++i)
for (int j = 1; j <= i; ++j)
f[i][j] = 1 - (f[i - 1][j] + f[i - 1][j - 1]) / 2.0;
for (scanf("%d", &t); t && scanf("%d", &n); --t, tot = 0)
{
for (int i = 1; i <= n; ++i)
scanf("%d", &m), tot += f[n][i] * m;
printf("%.3lf\n", tot);
}
return 0;
}