描述 Description
有两行自然数,UP[1..N],DOWN[1..M],如果UP[I]=DOWN[J]=K,那么上行的第I个位置的数就可以跟下行的第J个位置的数连一条线,称为一条K匹配,但是同一个位置的数最多只能连一条线。另外,每个K匹配都必须且至多跟一个L匹配相交且K≠L 。现在要求一个最大的匹配数。
例如:以下两行数的最大匹配数为8
输入格式 Input Format
第一行有两个正整数N和M。第二行N个UP的自然数,第三行M个DOWN的自然数。其中0<N、M<=200,UP、DOWN的数都不超过32767。
输出格式 Output Format
一个整数:最大匹配数
样例输入 Sample Input
样例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
样例2:
4 4
1 1 3 3
1 1 3 3
样例输出 Sample Output
样例1:
8
样例2:
0
时间限制 Time Limitation
2s
注释 Hint
2s
来源 Source
经典问题
就是一道DP问题,以为数据比较小就是一个双进程的DP裸题。因为你要求出交错的最多数,所以我们认为上面的数列为a[i],下面则为b[i];
而你要使数列上下匹配得到的两两交叉最多,所以要使交叉最多。我们可以用两重循环枚举两个队列里面的所有数,假设枚举到a[i],则在
b[]数组中从i开始前找最近的和a[i]相等的数然后返回这个数字的位置即可。这样我们状态转移方程f[i][j]就和f[i-1][j],f[i-1][j-1],f[x-1][y-1],所以
直接求最大值就行了
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 using namespace std; 7 int a[310],b[310]; 8 int dp1[310][310],dp2[310][310]; 9 int f[310][310]; 10 int work(int c[],int now,int v) 11 { 12 for(int i=now-1;i>=1;i--) 13 if(c[i]==v) 14 return i; 15 return 0; 16 } 17 int main() 18 { 19 int n,m; 20 cin>>n>>m; 21 for(int i=1;i<=n;i++) 22 cin>>a[i]; 23 for(int i=1;i<=m;i++) 24 cin>>b[i]; 25 memset(f,0,sizeof(f)); 26 for(int i=1;i<=n;i++) 27 { 28 for(int j=1;j<=m;j++) 29 { 30 f[i][j]=max(f[i][j-1],f[i-1][j]); 31 if(a[i]==b[j]) 32 continue; 33 int x=work(a,i,b[j]); 34 int y=work(b,j,a[i]); 35 if(x>0&&y>0) 36 { 37 f[i][j]=max(f[i][j],f[x-1][y-1]+2); 38 } 39 } 40 } 41 cout<<f[n][m]<<endl; 42 return 0; 43 }