題面php
這是一道DP神題,直到我寫下這句題解時也沒有想明白……ios
首先,這道題要咱們求全部(不一樣輸出序列的方案數)的平方和,因而咱們固然就想到求全部不一樣輸出序列的方案數……(大霧) 。這道題一個巧妙的地方就在於對問題的轉化。(如下摘自BYVoid大神的題解)c++
假設同時有兩我的X & Y在玩這個遊戲,設X從up取了i個珠子(不必定連續),從down取了j個珠子,取出來的珠子組成的序列爲Q,操做序列爲x,Y從up取了k個珠子,從down取了l個珠子,取出來的珠子組成的序列也爲Q,操做序列爲y,那麼咱們就獲得了一個有序對(x,y),f[i][j][k][l]即表示有序對(x,y)的數量。兩個有序對不相同當且僅當x和y不一樣時相同。優化
下面證實f[i][j][k][l]即爲所求。spa
已知:取出珠子的序列爲Q,x和y分別爲一種取珠方法(可相同), 取出Q的方案數爲a;code
求證:有序對(x,y)的數量等於a2。blog
由於取出Q的方案數爲a,因此x & y都有a種取值,且x & y彼此獨立,故對於x的每個取值,y都有a種取值,故有序對(x,y)的數量爲a2,命題得證。遊戲
博主是個超級大傻*,連空間優化到n2都不會,請各路大神指教。get
1 #include <map> 2 #include <set> 3 #include <cmath> 4 #include <queue> 5 #include <stack> 6 #include <cstdio> 7 #include <string> 8 #include <vector> 9 #include <cstring> 10 #include <complex> 11 #include <cstdlib> 12 #include <iostream> 13 #include <algorithm> 14 #define rg register 15 #define ll long long 16 using namespace std; 17 18 inline int gi() 19 { 20 rg int r = 0; rg bool b = 1; rg char c = getchar(); 21 while (c < '0' || c > '9') { if (c == '-') b = 0; c = getchar(); } 22 while (c >= '0' && c <= '9') { r = r * 10 + c - '0', c = getchar(); } 23 if (b) return r; return -r; 24 } 25 26 const int inf = 2147483647, N = 505, MOD = 1024523; 27 int n,m,f[N][N][N]; 28 char S[N],X[N]; 29 30 inline void input() 31 { 32 freopen ("!.in", "r", stdin); 33 n=gi(), m=gi(); 34 scanf("%s%s",S+1,X+1); 35 } 36 37 inline void output() 38 { 39 freopen ("!.out", "w", stdout); 40 printf("%d\n",f[n][m][n]); 41 } 42 43 inline void cal(int &t,int d) { t+=d; if (t >= MOD) t-=MOD; } 44 45 inline void solve() 46 { 47 int i,j,k,l,tmp; 48 f[0][0][0]=1; 49 for (i=0; i<=n; i++) 50 for (j=0; j<=m; j++) 51 for (k=0; k<=n; k++) 52 { 53 tmp=f[i][j][k], l=i+j-k; 54 if (!tmp || !l || l > m) continue; 55 if (S[i+1] == S[k+1]) 56 cal(f[i+1][j][k+1],tmp); 57 if (X[j+1] == S[k+1]) 58 cal(f[i][j+1][k+1],tmp); 59 if (S[i+1] == X[l+1]) 60 cal(f[i+1][j][k],tmp); 61 if (X[j+1] == X[l+1]) 62 cal(f[i][j+1][k],tmp); 63 } 64 } 65 66 int main() 67 { 68 input(); 69 solve(); 70 output(); 71 return 0; 72 }
Updateinput
博主終於會把空間優化到n^2辣!!!
PS:記得要清零!!!
1 #include <bits/stdc++.h> 2 #define rg register 3 #define ll long long 4 using namespace std; 5 6 inline int gi() 7 { 8 rg int r = 0; rg bool b = 1; rg char c = getchar(); 9 while (c < '0' || c > '9') { if (c == '-') b = 0; c = getchar(); } 10 while (c >= '0' && c <= '9') { r = r * 10 + c - '0', c = getchar(); } 11 if (b) return r; return -r; 12 } 13 14 const int inf = 2147483647, N = 505, MOD = 1024523; 15 int n,m,f[2][N][N]; 16 char S[N],X[N]; 17 18 inline void input() 19 { 20 n=gi(), m=gi(); 21 scanf("%s%s",S,X); 22 } 23 24 inline void cal(rg int &t,rg int d) 25 { 26 t+=d; 27 if (t >= MOD) 28 t-=MOD; 29 } 30 31 inline void solve() 32 { 33 rg int i,j,k,p,q,l,r,now,lst; 34 f[0][0][0]=1, now=0; 35 for (k=0; k<n+m; ++k) //枚舉一共選了多少個,由於每次更新都會多選一個,因此只需枚舉到 n+m-1 36 { 37 l=max(k-m,0), r=min(k,n); //計算 S 管道取珠的數量範圍 38 lst=now, now^=1; 39 for (i=l; i<=r; ++i) //分別枚舉序列 x,y 40 for (j=l; j<=r; ++j) 41 { 42 p=k-i, q=k-j; //i,j 表示 S 管道取的數量,p,q表示 X 管道的數量 43 if (!f[lst][i][j]) 44 continue; 45 if (S[i] == S[j]) 46 cal(f[now][i+1][j+1],f[lst][i][j]); 47 if (S[i] == X[q]) 48 cal(f[now][i+1][j],f[lst][i][j]); 49 if (X[p] == S[j]) 50 cal(f[now][i][j+1],f[lst][i][j]); 51 if (X[p] == X[q]) 52 cal(f[now][i][j],f[lst][i][j]); 53 f[lst][i][j]=0; //每次更新後要記得清零 54 } 55 } 56 printf("%d\n",f[now][n][n]); 57 } 58 59 int main() 60 { 61 input(); 62 solve(); 63 return 0; 64 }