1. 程式人生 > >CF 570E dp遞推

CF 570E dp遞推

題意:給一個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;
}