http://www.javashuo.com/article/p-gjwhuanu-ew.htmlphp
這道題是大連的某一年的現場賽的題hdu-4055
,,,剛開始作線性dp的題,,看了好半天才看懂解法,,html
題目的意思就是給出一個僅有1~n組成的序列的關係s:'I'表示 \(a[i+1]>a[i]\),'D'表示 \(a[i+1] < a[i]\),,'?'表示均可以,,而後問你全部可能的狀況的總數,,c++
用 \(dp[i][j]\) 表示長度爲i而且僅由1~i組成的序列以j結尾時的種類數,,spa
當 \(s[i]= ?\) 時,,當前點的可能狀況就是前面全部狀況的和,即 \(dp[i][j]=\sum_{k=1}^{i-1}dp[i-1][k]\).net
當 \(s[i]=I\) 時,,由於第i位固定就爲j了,而且前一位要知足小於等於j,因此就要找出全部長度爲i-1且結尾小於等於j-1的狀況的和,,即: \(dp[i][j]=\sum_{k=1}^{j-1}dp[i-1][k]\)code
當 \(s[i]=D\) 時,,和等於I的狀況相反,,也就是要找到全部長度爲i-1且最後一位大於j的種類數(同時要小於i-1),,也就是說能夠直接用?的種類數減去I的種類數,,即: \(dp[i][j]=\sum_{k=j}^{i-1}dp[i-1][k]=\sum_{k=1}^{i-1}dp[i-1][k]-\sum_{k=1}^{j-1}dp[i][k]\)htm
假定每次使第i位爲j時,前面大於等於j的值都加一,,這樣保證前i個數都出現一次,,同時i-1變成了i,,j變成了j+1,,j就放在了後面,,因此遍歷中的k是從j~i-1,,,blog
參考ci
最後用前綴和維護一下那個和,空間換時間
//hdu //#include <bits/stdc++.h> #include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> #include <algorithm> #include <queue> #define aaa cout<<233<<endl; #define endl '\n' #define pb push_back using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f;//1061109567 const ll linf = 0x3f3f3f3f3f3f3f; const double eps = 1e-6; const double pi = 3.14159265358979; const int maxn = 1e3 + 5; const int maxm = 2e5 + 5; const ll mod = 1e9 + 7; int dp[maxn][maxn], sum[maxn][maxn]; char s[maxn]; int main() { // freopen("233.in" , "r" , stdin); // freopen("233.out" , "w" , stdout); // ios_base::sync_with_stdio(0); // cin.tie(0);cout.tie(0); while(~scanf("%s", s + 2)) { int len = strlen(s + 2); memset(dp, 0, sizeof dp); memset(sum, 0, sizeof sum); dp[1][1] = sum[1][1] = 1; for(int i = 2; i <= len + 1; ++i) { for(int j = 1; j <= i; ++j) { if(s[i] == 'I') dp[i][j] = sum[i - 1][j - 1]; if(s[i] == 'D') dp[i][j] = (sum[i - 1][i - 1] - sum[i - 1][j - 1] + mod) % mod; if(s[i] == '?') dp[i][j] = sum[i - 1][i - 1]; sum[i][j] = (dp[i][j] + sum[i][j - 1]) % mod; } } printf("%d\n", sum[len + 1][len + 1]); } return 0; }
(end)