洛谷 P1758 [NOI2009] 管道取珠(DP)

题目链接

https://www.luogu.com.cn/problem/P1758

思路

对于 ∑ a i 2 \sum a_{i}^{2} ai2可以换一种理解方式:两个人在两个独立的管道中取球,输出序列相同的方案数。

因为对于每一种输出序列,第一个人有 a i a_{i} ai种方案,第二个人有 a i a_{i} ai种方案,那么两个人输出序列相同的方案总数就是​ ∑ a i 2 \sum a_{i}^{2} ai2

我们定义 f [ k ] [ i ] [ j ] f[k][i][j] f[k][i][j]表示两个人都各取了 k k k个球,第一个人在上管道取了 i i i个球,第二个人在上管道取了 j j j个球,两个人的输出序列相同的方案数。最后的答案就是 f [ n + m ] [ n ] [ m ] f[n+m][n][m] f[n+m][n][m]

因为两个人取的球的数目相同才能发生转移,所以讨论两个人当前从哪个管道中取的球,状态转移方程为:

  • f [ k ] [ i ] [ j ] + = f [ k − 1 ] [ i − 1 ] [ j − 1 ] × ( a [ i ] = = a [ j ] ) f[k][i][j] += f[k-1][i - 1][j - 1] \times (a[i] == a[j]) f[k][i][j]+=f[k1][i1][j1]×(a[i]==a[j])
  • f [ k ] [ i ] [ j ] + = f [ k − 1 ] [ i − 1 ] [ j ] × ( a [ i ] = = b [ k − j ] ) f[k][i][j] += f[k-1][i - 1][j] \times (a[i] == b[k - j]) f[k][i][j]+=f[k1][i1][j]×(a[i]==b[kj])
  • f [ k ] [ i ] [ j ] + = f [ k − 1 ] [ i ] [ j − 1 ] × ( b [ k − i ] = = a [ j ] ) f[k][i][j] += f[k-1][i][j - 1] \times (b[k - i] == a[j]) f[k][i][j]+=f[k1][i][j1]×(b[ki]==a[j])
  • f [ k ] [ i ] [ j ] + = f [ k − 1 ] [ i ] [ j ] × ( b [ k − i ] = = b [ k − j ] ) f[k][i][j] += f[k-1][i][j] \times (b[k - i] == b[k - j]) f[k][i][j]+=f[k1][i][j]×(b[ki]==b[kj])

时间复杂度: O ( n 3 ) O(n^3) O(n3)

代码

#define _USE_MATH_DEFINES
#include 

using namespace std;

constexpr long double pi = static_cast<long double>(M_PI);

#define int long long
#define double long double
#define endl "\n"

typedef long long i64;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair<int, int> pii;
typedef tuple<int, int, int> tiii;

const int N = 2e3 + 5, M = 5e3 + 15;
const int mod = 1024523;
const i64 inf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;

std::mt19937 rnd(time(0));

int n, m;
char a[N], b[N];
int f[2][N][N];
void solve(int test_case)
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= m; i++)
    {
        cin >> b[i];
    }
    reverse(a + 1, a + 1 + n);
    reverse(b + 1, b + 1 + m);
    f[0][0][0] = 1;
    for (int k = 1; k <= n + m; k++)
    {
        int idx = k & 1;
        for (int i = 0; i <= n; i++)
        {
            for (int j = 0; j <= n; j++)
            {
                f[idx][i][j] = 0;
            }
        }
        for (int i = max(0LL, k - m); i <= min(n, k); i++)
        {
            for (int j = max(0LL, k - m); j <= min(n, k); j++)
            {
                if (i && j && a[i] == a[j])
                {
                    f[idx][i][j] += f[idx ^ 1][i - 1][j - 1];
                    f[idx][i][j] %= mod;
                }
                if (i && k - j && a[i] == b[k - j])
                {
                    f[idx][i][j] += f[idx ^ 1][i - 1][j];
                    f[idx][i][j] %= mod;
                }
                if (k - i && j && b[k - i] == a[j])
                {
                    f[idx][i][j] += f[idx ^ 1][i][j - 1];
                    f[idx][i][j] %= mod;
                }
                if (k - i && k - j && b[k - i] == b[k - j])
                {
                    f[idx][i][j] += f[idx ^ 1][i][j];
                    f[idx][i][j] %= mod;
                }
            }
        }
    }
    cout << f[(n + m) & 1][n][n] << endl;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int test = 1;
    // cin >> test;
    for (int i = 1; i <= test; i++)
    {
        solve(i);
    }
    return 0;
}

你可能感兴趣的:(ACM—DP,动态规划,算法)