【dp】幣

題目獻上(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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章