1. 程式人生 > >noip模擬賽 尋寶之後

noip模擬賽 尋寶之後

str 記錄 char family 狀態 回文字符串 else ++ 路徑

題目背景

還記得NOIP2011的尋寶嗎?6年之後,小明帶著他的妹子小芳,再次走上了尋寶的道路。

然而這次他們尋寶回來之後,小明被困在了一個迷宮中。

題目描述

迷宮是一個n*m的字符矩陣。

小明在這個矩陣的左上角,只能向下和向右走,去和在矩陣右下角的小芳會合。

小明必須將他走過的路徑上的,經過的字符收集起來。如果到右下角時他收集到的這些字符連在一起是回文的,那麽他就能夠走出這個迷宮,否則他就會掉進陷阱出不來。

小明想知道有多少條路徑能夠讓他走出這個迷宮。由於答案可能很大,請對1000000007取模。

輸入輸出格式

輸入格式:

第一行兩個整數n和m。

接下來n行,每行m個字符表示這個矩陣,全部均為小寫字母

輸出格式:

輸出一行一個整數表示答案

輸入輸出樣例

輸入樣例#1:
3 4
aaab
baaa
abba
輸出樣例#1:
3

說明

對於20%的數據,滿足n?m10

對於另外10%的數據,滿足字符都是a

對於70%的數據,滿足n,m60

對於100%的數據,滿足n,m500

分析:一道比較考驗基本功的dp題.

看到題面就應該能想到狀態該怎麽表示了,設f[i][j][k][l]表示小明走到了(i,j),小芳走到了(k,l)的方案數,那麽該怎麽轉移呢?顯然不能順著推,因為不知道走過的字符串是啥,如果記錄的話會有後效性,那麽根據回文字符串的特點,小明從起點走,小芳從終點走,如果兩個人所在地方的字符是一樣的才能繼續走,直到兩個人碰面,答案累加,因為空間問題可以過70分.

後30%的數據只能開下500*500的數組,也就是說我們只能夠保存兩維.那麽我們可以保存j和l,枚舉當前走了多少步,因為方向一定,所以i和k能夠推導出來。因為只保存了列的情況,如果直接推的話會計算重復,那麽再開一個數組g,記錄前一步的狀態,f從g轉移而來就可以了.

最後統計答案,如果n+m-1是奇數,那麽最後匯合的地點一定是同一行,否則有可能是同一行,也有可能相差一行.

總結:這一類dp問題特征就是給你一個n*m的圖,規定走的方向,讓你求某些值.狀態表示比較有規律,一般就是設f[i][j]表示走到了(i,j)的答案.如果題目變通一下讓你走兩次,那麽可以開四維數組來表示狀態.有時候也需要變通一下枚舉的順序,依題目而定,規定了走的方向,我們可以只用保存3維就可以推出第4維,可以優化時間,我們也可以保存2維,這樣就需要一個輔助數組記錄上一步的狀態,既優化了時間,也優化了空間.

#include <stack>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int mod = 1000000007;

int n, m,f[510][510],g[510][510];
int dx[4] = { 1, 0, -1, 0 }, dy[4] = { 0, 1, 0, -1 };
long long ans = 0;
char s[510][510];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
        scanf("%s", s[i] + 1);
    if (s[1][1] == s[n][m])
        f[1][m] = 1;
    else
        f[1][m] = 0;
    for (int t = 1; t < (n + m) / 2; t++)
    {
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= m; j++)
            {
                g[i][j] = f[i][j];
                f[i][j] = 0;
            }

        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= m; j++)
                if (g[i][j])
                {
                    int y11 = i, y2 = j, x1 = t - i + 1, x2 = n - t + m - j + 1;
                    for (int k = 0; k < 2; k++)
                    {
                        for (int l = 2; l < 4; l++)
                        {
                            int nx1 = x1 + dx[k], ny1 = y11 + dy[k];
                            int nx2 = x2 + dx[l], ny2 = y2 + dy[l];
                            if (nx1 > n || ny1 > m || nx2 < 1 || ny2 < 1)
                                continue;
                            if (s[nx1][ny1] == s[nx2][ny2])
                                f[ny1][ny2] = (f[ny1][ny2] + g[i][j]) % mod;
                        }
                    }
                }
    }
    if ((n + m - 1) & 1)
    {
        for (int i = 1; i <= m; i++)
            ans = (ans + f[i][i]) % mod;
    }
    else
    {
        for (int i = 1; i <= m; i++)
        {
            ans = (ans + f[i][i]) % mod;
                ans = (ans + f[i][i + 1]) % mod;
        }
    }
    printf("%d\n", ans);
return 0;
}

noip模擬賽 尋寶之後