パイプビーズ抽出(NOI2009、線形DP)

1.トピックリンク

\ quad パイプビーズ抽出

2.トピックの主なアイデア

\ quad 元の質問は素晴らしいです。
ここに写真の説明を挿入

3.分析

\ quad この質問は素晴らしい片手変換です、私はそれを学びました~~

\ quad この問題では、a [i]を最終シーケンスの特定の数の解としますが、[i] 2 a [i] ^ 2を解きます。a [ i ]2.明らかに、2つの関係を見つける必要があります。

\ quad 2つの同一の独立したシステムがあり、2人が同時に操作していると仮定すると、特定の最終シーケンスFについて、操作後に2人が取得した解の数がFと同じであることを証明することは難しくありません[i] 2 a [i] ^ 2a [ i ]2

\ quad この変換ステップの後、dpをスパイシーにすることができます~~(しかしガチョウはそうではありません...)

\ quad dp [i] [j] [k] [l]を、パイプ1からiボールを取得する最初の人、パイプ2からjボールを取得する最初の人、パイプ1からjボールを取得する2番目の人とします。 k個のボール、2人目はパイプ2からl個のボールを取り出し、現在同じシーケンスのスキームの数が構成されています。

\ quad しかし、そのような状態は時間と空間が破裂することを意味します.2つのシーケンスが同じであるため、i + j == k + lであるため、最初の次元でのローリング最適化に加えて、1つの次元を省略できます。 ACの時間と空間を入力してください!

\ quad 状態遷移方程式は

if(s[i + 1] == s[k + 1]) f[p^1][j][k + 1] = (f[p^1][j][k + 1] + f[p][j][k]) % mod;
if(s[i + 1] == t[l + 1]) f[p^1][j][k] = (f[p^1][j][k] + f[p][j][k]) % mod;
if(t[j + 1] == s[k + 1]) f[p][j + 1][k + 1] = (f[p][j + 1][k + 1] + f[p][j][k]) % mod;
if(t[j + 1] == t[l + 1]) f[p][j + 1][k] = (f[p][j + 1][k] + f[p][j][k]) % mod;

さらに、以下のサンプルペーパーのように、少し最適化を追加できます

if(!f[p][j][k])    continue;
if(s[i + 1] == s[k + 1]) f[p^1][j][k + 1] = (f[p^1][j][k + 1] + f[p][j][k]) % mod;
if(s[i + 1] == t[l + 1]) f[p^1][j][k] = (f[p^1][j][k] + f[p][j][k]) % mod;
if(t[j + 1] == s[k + 1]) f[p][j + 1][k + 1] = (f[p][j + 1][k + 1] + f[p][j][k]) % mod;
if(t[j + 1] == t[l + 1]) f[p][j + 1][k] = (f[p][j + 1][k] + f[p][j][k]) % mod;

\ quad テスト後、制限時間は大幅に最適化されました。
ここに写真の説明を挿入

4.コードの実装

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int M = (int)5e2;
const ll mod = (ll)1024523;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;

int n, m;
char s[M + 5], t[M + 5];
int f[2][M + 5][M + 5];

int main()
{
    
    
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    scanf("%d %d", &n, &m);
    scanf("%s %s", s + 1, t + 1);
    reverse(s + 1, s + n + 1); reverse(t + 1, t + m + 1);
    f[0][0][0] = 1;
    for(int i = 0, p = 0; i <= n; ++i, p ^= 1)
    {
    
    
        for(int j = 0; j <= m; ++j)
        {
    
    
            for(int k = 0; k <= n; ++k)
            {
    
    
                int l = i + j - k;
                if(l < 0 || l > m) continue;
                if(!f[p][j][k])    continue;
                if(s[i + 1] == s[k + 1]) f[p^1][j][k + 1] = (f[p^1][j][k + 1] + f[p][j][k]) % mod;
                if(s[i + 1] == t[l + 1]) f[p^1][j][k] = (f[p^1][j][k] + f[p][j][k]) % mod;
                if(t[j + 1] == s[k + 1]) f[p][j + 1][k + 1] = (f[p][j + 1][k + 1] + f[p][j][k]) % mod;
                if(t[j + 1] == t[l + 1]) f[p][j + 1][k] = (f[p][j + 1][k] + f[p][j][k]) % mod;
                f[p][j][k] = 0;
            }
        }
    }
    printf("%d\n", f[(n + 1) & 1][m][n]);
    return 0;
}

おすすめ

転載: blog.csdn.net/The___Flash/article/details/106413451