Uva1633_分階段dp、狀態dp
動態規劃訓練3——Uva1633
Dyslexic Gollum
‘Light, light of Sun and Moon, he still feared and hated, and he always will, I think; but
he was cunning. He found he could hide from daylight and moonshine, and make his way
swiftly and softly by dead of night with his pale cold eyes, and catch small frightened or
unwary things. He grew stronger and bolder with new food and new air. He found his way
into Mirkwood, as one would expect.’
Gandalf, describing Gollum after he ventured forth from Moria.
Gollum has spent half a millennium in the long darkness of Moria, where his eyes grew used to the
dark, and without caring for reading or writing, he became dyslexic. Indeed, as much as he hates the
Moon and the Sun, he also hates strings with long palindromes in them.
Gollum has a tolerance level of K, which means that he can read a word so long as it does not
contain any palindromic substring of length K or more. Given the values N and K, return how many
BINARY strings of length N can Gollum tolerate reading.
Input
The rst line contains T, the number of test cases.
Each test case consists of one line containing 2 integers, N and K.
Output
For each test case, output the answer modulo 1,000,000,007.
Constraints:
1 T 100
1 N 400
1 K 10
Notes/Explanation of Sample Input:
For the rst test case, 01 and 10 are the valid binary strings, while 00 and 11 are invalid.
For the second test case, 001, 011, 100, 110 are the valid binary strings.
For the third test case, all possible binary strings of length 3 are valid.
Sample Input
3
2 2
3 3
3 4
Sample Output
2
4
8
讀完本題較容易想到分階段的dp:每個階段在字串末尾添上0或1。所以可設定一個狀態變數i表示當前字串的長度。
此題要求字串中迴文串的長度小於k,則我們規定每個階段得到的字串中迴文串最長為k-1。據此規則,我們可以得到,每個階段引入的字元至多導致長度k+1的迴文串,表明引入的字元至多對末尾長度為k+1的串產生影響。由此我們可再引入一個狀態量j,記錄末尾k+1位串的二進位制狀態。再由此題給出k<=10,為了方便,我們取j最大2^11-1。
綜上,最終我們的狀態設定為(字串長度i,字串最後11位j),資料範圍滿足要求。
有關狀態轉移:(j中二進位制低位表示字串末位)
當j中迴文串長度大於等於k時,
dp[i][j]=0;
否則,
dp[i][j]=dp[i-1][j>>1]+dp[i-1][(j>>1)+(1<<10)];
有關實現細節:
本題要判斷j中迴文串的長度,因此可用二維陣列記錄長度小於12的二進位制數中迴文串的長度。
可用簡單的動態規劃快速實現。
同時,這樣還可直接得到i<=11時,dp[i][j]的值。只需對i>11時進行轉移。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <math.h>
#include <algorithm>
using namespace std;
const int BASE = 1e9 + 7;
int pali[15][3000] = { 0 }, dp[15][405][3000] = { 0 };
int main(void) {
pali[1][0] = pali[1][1] = pali[2][1] = pali[2][2] = 1;
pali[2][0] = pali[2][3] = 2;
for (int i = 3; i <= 11; ++i)
for (int j = 0, t1, t2; j <= (1 << i) - 1; ++j) {
t1 = 1 & j, t2 = j >> (i - 1);
if (t1 == t2 && pali[i - 2][(j&((1 << (i - 1)) - 1)) >> 1] == i - 2) pali[i][j] = i;
else pali[i][j] = max(pali[i - 1][j >> 1], pali[i - 1][j&((1 << (i - 1)) - 1)]);
}
for (int k = 2; k <= 10; ++k)
for (int n = 1; n <= 11; ++n)
for (int j = 0; j <= (1 << n) - 1; ++j)
if (pali[n][j] < k) dp[k][n][j] = 1;
for (int k = 2; k <= 10; ++k)
for (int n = 12; n <= 400; ++n)
for (int j = 0; j <= (1 << 11) - 1; ++j)
if (pali[11][j] >= k) dp[k][n][j] = 0;
else dp[k][n][j] = (dp[k][n - 1][j >> 1] + dp[k][n - 1][(j >> 1) + (1 << 10)])%BASE;
int T, N, K;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &N, &K);
int ans = 0;
int n = min(11, N);
for (int i = 0; i <= (1 << n) - 1; ++i)
ans = (ans + dp[K][N][i]) % BASE;
cout << ans << endl;
}
//system("pause");
return 0;
}