CF 570E dp遞推
阿新 • • 發佈:2018-11-05
題意:給一個n*m的小寫字母地圖,找從(1,1)到(n,m)點非升路徑,且路徑組成的字串是迴文串的路徑個數。
思路:
考慮從兩端同時走,然後在中間相遇,走的步數是確定的,為n+m-1,
所以只要兩邊同時走(n+m)/2步就行;
考慮dp遞推,只要條件限制好,是可以遞推的
設dp[step][x1][y1][x2][y2]為走step步到達p1,p2兩點匹配的路徑個數。
轉移方程就是
dp[step][x1][y1][x2][y2]=dp[step-1][x1][y1-1][x2][y2-1]+dp[step-1][x1][y1-1][x2-1][y2]+
dp[step-1][x1-1][y1][x2-1][y2]+dp[step][x1-1][y1][x2][y2-1];
這樣的話空間時間都受不了,有些是沒有必要的,比如step可以用滾動陣列變成2,然後設一個計數器
step知道,x知道,y就確定,所以沒必要用y,這樣空間就是5e5,時間是1e7
#include<bits/stdc++.h> using namespace std; const int mo=1e9+7; const int N=510; typedef long long ll; ll dp[2][N][N]; char s[N][N]; int main(){ int n, m; scanf("%d%d", &n, &m); for(int i=1; i<=n; i++){ scanf("%s", s[i]+1); } int up; if((n+m)&1) up=(n+m-1)>>1; else up=(n+m)>>1; int cur=0, nxt=1; if(s[1][1]==s[n][m]) dp[cur][1][n]=1; for(int step=2; step<=up; step++){ for(int i=0; i<=n; i++) for(int j=0; j<=n; j++) dp[nxt][i][j]=0; //printf("step: %d\n", step); int ll=min(n, step); for(int x1=1; x1<=ll; x1++){ int y1=step-x1+1; int hh=max(1, n-step+1); for(int x2=n; x2>=hh; x2--){ int y2= m-(step-(n-x2+1)); //printf("(%d,%d) (%d,%d)\n", x1, y1, x2, y2); if(x1<=x2 && s[x1][y1]==s[x2][y2]){ dp[nxt][x1][x2]=dp[cur][x1-1][x2] + dp[cur][x1-1][x2+1] + dp[cur][x1][x2] + dp[cur][x1][x2+1]; dp[nxt][x1][x2]%=mo; //printf("%lld %lld %lld %lld\n", dp[cur][x1-1][x2], dp[cur][x1-1][x2+1], dp[cur][x1][x2], dp[cur][x1][x2+1]); //printf("(%d, %d) (%d, %d) %lld\n", x1, y1, x2, y2, dp[nxt][x1][x2]); } } } swap(cur, nxt); } ll ans=0; for(int i=1; i<=n; i++){ if((n+m)&1){ ans=(ans+dp[cur][i][i+1]+dp[cur][i][i])%mo; } else{ ans=(ans+dp[cur][i][i])%mo; } } printf("%I64d\n", ans); return 0; }