卧薪尝胆,厚积薄发。
NOI2009 管道取珠
Date: Tue Oct 02 16:47:04 CST 2018 In Category: NoCategory

Description:

有一上一下两个管道,每个管道里有不同数量的深色或浅色珠子,每此可以从两个管道之一的队头取一个珠子,最后会形成一个深浅珠子的序列,设第 $i$ 种最终序列有 $k_i$ 种形成方式,求 $\sum k_i$ 。
$1\leqslant n \leqslant500$

Solution:

直接统计没法统计,因为 $N^2$ 这个东西不能线性相加,不能线性相加就不能简单地合并统计方案,观察一下 $N^2$ 有什么组合含义,可以看成形成两个序列完全相同的方案数,于是有这样一个 $\text{DP}$ : $f[i][j][k][l]$ 表示第一种方案第一个管道 $i$ 之后的都已经处理了,第二个管道 $j$ 之后都已经被处理了, $k$ 和 $l$ 同理表示第二种方案,发现 $i+j=k+l$ 所以可以省掉一维,转移就是看哪对珠子相同然后累加一下,要用滚动数组。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MAXN 510
int n,m;
inline char getc()
{
register char c = getchar();
while(c != 'A' && c != 'B')c = getchar();
return c;
}
char a[MAXN],b[MAXN];
int f[2][MAXN][MAXN];
#define MOD 1024523
int main()
{
scanf("%d%d",&n,&m);
for(register int i = 1;i <= n;++i)a[i] = getc();
for(register int i = 1;i <= m;++i)b[i] = getc();
register int cur = 0;
f[cur][m][n] = 1;
for(register int i = n;i >= 0;--i)
{
for(register int j = m;j >= 0;--j)
{
for(register int k = n;k >= 0;--k)
{
register int l = i + j - k;
if(l > m)break;
if(l < 0)continue;
if(a[i + 1] == a[k + 1])f[cur][j][k] = (f[cur][j][k] + f[cur ^ 1][j][k + 1]) % MOD;
if(b[j + 1] == b[l + 1])f[cur][j][k] = (f[cur][j][k] + f[cur][j + 1][k]) % MOD;
if(a[i + 1] == b[l + 1])f[cur][j][k] = (f[cur][j][k] + f[cur ^ 1][j][k]) % MOD;
if(b[j + 1] == a[k + 1])f[cur][j][k] = (f[cur][j][k] + f[cur][j + 1][k + 1]) % MOD;
}
}
cur = cur ^ 1;
memset(f[cur],0,sizeof(f[cur]));
}
cout << f[cur ^ 1][0][0] << endl;
return 0;
}
In tag: DP-DP
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡