#P2030. 交错匹配
#P2030. 交错匹配
题目描述
有两行自然数,UP[1..N],DOWN[1..M],如果UP[I]=DOWN[J]=K,那么上行的第I个位置的数就可以跟下行的第J个位置的数连一条线,称为一条K匹配,但是同一个位置的数最多只能连一条线。另外,每个K匹配都必须且至多跟一个L匹配相交且K≠L!现在要求一个最大的匹配数。 例如:以下两行数的最大匹配数为8
输入格式
第一行有两个正整数N和M。
第二行N个UP的自然数,第三行M个DOWN的自然数。其中0<N、M<=200,UP、DOWN的数都不超过32767。
输出格式
最大匹配数输出
样例
输入数据 1
12 11
1 2 3 3 2 4 1 5 1 3 5 10
3 1 2 3 2 4 12 1 5 5 3
输出数据 1
8
输入数据 2
4 4
1 1 3 3
1 1 3 3
输出数据 2
0
Solution
首先注意到,题目给定的数据规模不大,只有 \(200\) ,因此可以考虑用稍微暴力一点的算法。
提前预处理出与 \(down_i\) 相同的 \(up_i\) 的位置,存储在 \(same_i\) 中,然后 \(\text{DP}\) 。
设 \(f[i][j]\) 表示 \(up\) 前 \(i\) 个和 \(down\) 前 \(j\) 个能够构成的交错匹配数量。那么可以直接枚举交错匹配四个端点的位置,并尝试更新答案。
对于每个新的 \(f[i][j]\) ,答案可以从 \(f[i][j-1]\) 和 \(f[i-1][j]\) 继承过来。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<limits.h>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
template<typename T> void read(T &k)
{
k=0;
T flag=1;char b=getchar();
while (b<'0' || b>'9') {flag=(b=='-')?-1:1;b=getchar();}
while (b>='0' && b<='9') {k=(k<<3)+(k<<1)+(b^48);b=getchar();}
k*=flag;
}
const int _SIZE=2e2;
int n,m;
int up[_SIZE+5],down[_SIZE+5];
int same[_SIZE+5][_SIZE+5],f[_SIZE+5][_SIZE+5];
int main()
{
read(n);read(m);
for (int i=1;i<=n;i++) read(up[i]);
for (int i=1;i<=m;i++) read(down[i]);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (up[i]==down[j]) same[j][++same[j][0]]=i;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
f[i][j]=max(f[i-1][j],max(f[i][j-1],f[i][j]));
if (up[i]!=down[j]) continue;
for (int k=j+1;k<=m;k++)
if (down[k]==down[j]) continue;
else for (int l=1;l<=same[k][0];l++)
if (same[k][l]>=i) break;
else f[i][k]=max(f[i][k],f[same[k][l]-1][j-1]+2);
}
printf("%d\n",f[n][m]);
return 0;
}