題意:
給你一個硬幣,拋擲n次,問出現連續至少k個正面向上(H)的情況有多少種。
分析:
原題中問出現連續至少k個H的情況,很難下手。我們可以試着將問題轉化一下,設 dp[i][j] 表示拋擲i個硬幣出現連續至多 j 個H 的情況種數。
實際出現連續至少k個H,即出現連續k個H,k+1個H,…n個H的並集,等價於dp[n][n]-dp[n][k-1],即從連續至多n個H的情況(所有拋擲情況種類數)減去連續至多(k-1)個H的情況,這保證得到的所有情況一定至少有k個連續的H。
現在問題就變成了怎麼求dp[i][j]。
i <= j:
dp[i][j] = dp[i-1][j] × 2,即從上一階段得到的拋擲序列後面增加正和反兩種情況,如果出現連續的H個數大於j個,這種情況是非法的,但很顯然此時不會出現這種情況。當前位置隨便放。
i > j:
如果繼續用 dp[i][j] = dp[i-1][j] × 2 就不行了。因爲如果 從第 i - j 個到第 i - 1 全是H ,那麼這時候在第i個位置再加一個H,就會出現連續的H個數大於j個的非法狀態,所以我們需要減掉 從 i - j 到第 i - 1 全是H 的這種情況。那麼這種情況有多少種呢?我們考慮該狀態是如何轉移而來的。試想第 i - j - 1 個位置應該是什麼呢?很明顯只能是T。如果是H那就會出現非法狀態了。那在第 i - j - 1 之前的位置呢。無論H和T都可以,只要不出現連續的H個數大於j的非法狀態即可,這就是 dp[i-j-2][j]。
那麼這樣,dp[i][j] = dp[i-1][j] × 2 - dp[i-j-2][j]。
但這還是不夠的。我們之前的推導都是基於第i-j-1個位置一定存在的前提下(i>j不能保證第i-j-1個位置一定存在),那如果第i-j-1個位置不存在,第i-j-2個位置也就不存在,上述方程也就不成立了。但這種情況很好想,此時一定是i==j+1,從第1個位置到第j個位置全部都是H,只有這一種情況,所以方程變成 dp[i][j]=dp[i-1][j] × 2 - 1。
綜上,狀態轉移方程爲:
i<=j:dp[i][j] = dp[i-1][j] × 2;
i==j+1:dp[i][j] = dp[i-1][j] × 2 - 1;
i>j+1:dp[i][j] = dp[i-1][j] × 2 - dp[i-j-2][j]
ans=dp[n][n] - dp[n][k-1]
AC代碼:
import java.math.BigInteger;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
BigInteger a, b;
BigInteger dp[][] = new BigInteger[105][105];
//初始化
for (int i = 0; i <= 100; i++) {
dp[0][i] = BigInteger.valueOf(1);
dp[i][0] = BigInteger.valueOf(1);
dp[1][i] = BigInteger.valueOf(2);
}
for (int i = 1; i <= 100; i++) {
for (int j = 1; j <= 100; j++) {
if (i <= j)
dp[i][j] = dp[i - 1][j].multiply(BigInteger.valueOf(2));
else if (i == j + 1)
dp[i][j] = dp[i - 1][j].multiply(BigInteger.valueOf(2)).subtract(BigInteger.valueOf(1));
else
dp[i][j] = dp[i - 1][j].multiply(BigInteger.valueOf(2)).subtract(dp[i - j - 2][j]);
}
}
Scanner in = new Scanner(System.in);
while (in.hasNext()) {
a = BigInteger.valueOf(in.nextInt());
b = BigInteger.valueOf(in.nextInt()).subtract(BigInteger.valueOf(1));
System.out.println(dp[a.intValue()][a.intValue()].subtract(dp[a.intValue()][b.intValue()]));
}
in.close();
}
}