【USACO 2015 Open Gold】Palindromic Paths 動態規劃

空灰冰魂發表於2015-04-23

連結:

#include <stdio.h>
int main()
{
    puts("轉載請註明出處[vmurder]謝謝");
    puts("網址:blog.csdn.net/vmurder/article/details/45222487");
}

題意:

n×n

n \times n
的矩陣 左上角走到右下角會有一個長度 n+n+1
n+n+1
的字串,問有多少種走法使得路徑字串為迴文?

題解:

f(i,j,k,l)

f(i,j,k,l)
表示起點橫著走 i
i
步,豎著走 j
j
步,終點豎著走 k
k
步,橫著走 l
l
步時的迴文方案數。
然後跑動態規劃時 f(i,j,k,l)
f(i,j,k,l)
可以更新
f(i+1,j,k+1,l)f(i+1,j,k,l+1)f(i,j+1,k+1,l)f(i,j+1,k,l+1)
f(i+1,j,k+1,l)、f(i+1,j,k,l+1)、f(i,j+1,k+1,l)、f(i,j+1,k,l+1)

跑動態規劃的順序是 (i+j)
(i+j)
從小到大。

然後時間複雜度是 O(n4)

O(n^4)
,但是我們發現 (i+j==k+l)
(i+j==k+l)
,所以可以少列舉一個 l
l
,然後時空複雜度就變成 O(n3)
O(n^3)
了,但是這道題64MB,會MLE,然後我們發現 (i+j)
(i+j)
滿足滾動陣列的性質,空間複雜度變成了 O(n2)
O(n^2)

程式碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 505
#define mod 1000000007
using namespace std;
int n,f[2][N][N];
char s[N][N];
#define add(a,b) (a=(a+b)%mod)
int main()
{
    int i,j,k,l;
    int a,b,x,y;

    scanf("%d",&n);
    for(i=1;i<=n;i++)scanf("%s",s[i]+1);
    if(s[1][1]!=s[n][n])
    {
        puts("0");
        return 0;
    }
    int now=1,last=0;
    f[now][0][0]=1;
    for(int h=0;h<n;h++)
    {
        now^=1,last^=1;
        memset(f[now],0,sizeof f[now]);
        for(i=0;i<=h;i++)
        {
            j=h-i,a=1+i,b=1+j;
            for(k=0;k<=h;k++)
            {
                l=h-k,x=n-l,y=n-k;
                if(s[a+1][b]==s[x-1][y])add(f[now][i+1][ k ],f[last][i][k]);
                if(s[a+1][b]==s[x][y-1])add(f[now][i+1][k+1],f[last][i][k]);
                if(s[a][b+1]==s[x-1][y])add(f[now][ i ][ k ],f[last][i][k]);
                if(s[a][b+1]==s[x][y-1])add(f[now][ i ][k+1],f[last][i][k]);
            }
        }
    }
    int ans=0;
    for(i=0;i<n;i++)add(ans,f[last][i][i]);
    cout<<ans<<endl;
    return 0;
}

相關文章