题目描述
小 Z Z Z 英语课思考数学题,被英语老师发现啦。
英语老师:「我们定义一个回文串是正方读起来相同的字符串」。
小
Z
Z
Z :「这个简单,不就是像 abba
aba
这样的吗」。
英语老师:「现在给你一个长度为
n
n
n 的字符串,要你求出他的最长回文子序列」。
小
Z
Z
Z :「子序列是不连续的吧? 好的我知道了」。
小
Z
Z
Z 轻松的解决了这个问题,并把他修改了一下交给你。
现在一个字符串变成了
m
m
m 个数字,会魔法的小
Z
Z
Z 可以把一个数字
x
x
x 根据变换规则变成
y
y
y ,给定所有的变换规则,要你求出这个数字串的最长回文子序列。
输入格式
第一行输入 3 3 3 个正整数 n , k , m n, k, m n,k,m , k k k 是转换规则的个数。
第二行开始的 k k k 行,每行两个正整数 x x x 和 y y y ,表示数字 x x x 可以变成数字 y y y ,并且数字 y y y 可以变成数字 x x x 。注意,如果数字 x x x 可以变成数字 y y y ,并且数字 y y y 可以变成数字 z z z ,那么数字 x x x 也可以便成数字 z z z 。 x x x 可能等于 y y y ,同一对 ( x , y ) (x, y) (x,y) 可能重复出现。
最后一行输入 m m m 个正整数,表示题目中所提的数字串,每个数 ≤ n \le n ≤n 。
输出格式
输出一行一个数表示答案。
样例
样例输入1:
10 7 6
1 3
5 7
3 5
2 6
2 4
8 4
10 9
1 9 2 3 10 3
样例输出1:
5
数据范围
对于
20
%
20\%
20% 的数据,
n
,
k
,
m
≤
10
n, k, m \le 10
n,k,m≤10。
对于
40
%
40\%
40% 的数据,
n
,
k
,
m
≤
200
n, k, m \le 200
n,k,m≤200。
对于
100
%
100\%
100% 的数据,
n
≤
1
0
5
,
k
≤
1
0
6
,
m
≤
1
0
3
,
1
≤
x
,
y
≤
n
n \le 10^5, k \le 10^6, m \le 10^3, 1 \le x, y \le n
n≤105,k≤106,m≤103,1≤x,y≤n。
题解
对于可以转换的数字,把它们用一个数据结构进行维护。
40
%
40\%
40%:
最先可以想到用图来存,使用邻接矩阵,
f
x
,
y
f_{x, y}
fx,y 等于
1
1
1,说明
x
x
x 可以变成
y
y
y。
跑一遍 Floyd
,得出能变成的数字。
再跑一遍最长回文子序列即可。
100
%
100\%
100%:
使用并查集维护,将可以互相转化的数字合并。
接下来跑一遍最长回文子序列就行。
关于求最长回文子序列:
f
i
,
j
f_{i, j}
fi,j 表示从
i
i
i 到
j
j
j 的最长回文子序列长度。
转移方程:
- 普遍情况下, f i , j = max ( f i + 1 , j , f i , j − 1 ) f_{i, j} = \max(f_{i + 1, j}, f_{i, j - 1}) fi,j=max(fi+1,j,fi,j−1)。
- 如果 i i i 和 j j j 能转化, f i , j = max ( f i , j , f i + 1 , j − 1 + 2 ) f_{i, j} = \max(f_{i, j}, f_{i + 1, j - 1} + 2) fi,j=max(fi,j,fi+1,j−1+2)。
代码:
int n, k, m;
int u[100010];//并查集
int a[1010];
int f[1010][1010];
//get_father 求 x 属于的集合
int main(){
scanf("%d %d %d", &n, &k, &m);
//初始化
for(int i = 1; i <= n; ++ i){
u[i] = i;
}
for(int i = 1; i <= k; ++ i){
int x, y;
scanf("%d %d", &x, &y);
//合并
int t1 = get_father(x), t2 = get_father(y);
if(t1 == t2){
continue;
}
u[t1] = t2;
}
输入 a
//初始化 f
memset(f, 0, sizeof(f));
for(int i = 1; i <= m; ++ i){
f[i][i] = 1;
}
//区间 dp
for(int l = 1; l < m; ++ l){
for(int i = 1; i <= m - l; ++ i){
int j = i + l;
//[i, j]
f[i][j] = max(f[i + 1][j], f[i][j - 1]);
if(u[get_father(a[i])] == u[get_father(a[j])]){
f[i][j] = max(f[i][j], f[i + 1][j - 1] + 2);
}
}
}
printf("%d", f[1][m]);
}