ZOJ 1425 Crossed Matchings(LCS变形)
题目大意:
就是说给你两行数字,然后对于每一行的相同的数字进行连线,每次连接成功后,就算是1次成功,求最大的成功次数。当然,要成功的话,也是要满足一定的条件的:
1.在连接的过程中,不能用两根线连接了4个相同数字。
2.一个数字不能发出两条线。
解题思路:
刚看到这个题,以为是图论的知识,感觉自己拿不下,,(有点像是什么二分图匹配吧,用两个颜色给相邻的顶点染色,求最多能有多少种这样的匹配,,逃)
其实呢,这个有点像LCS,,,我第一次也没看出来,看了题解后,才明白的。
我们规定这样的状态: dp[i][j] 表示第一行前i个数字和第二行前j个数字的最大匹配数目.
由LCS的状态转移方程知道:dp[i][j] = max( dp[i-1][j],dp[i][j-1]);
那么我们由题目要求的定义知道,如果出现第1行的第i个字符和第二行的第j个字符不同的时候,也就是说a[i]!=b[j],那么,我们开始先定住a[i],从b[j]开始往前找,如果找到了a[i]==b[k]的位置,那就说明,在a[i]和b[k]之间就会有一条连线了。这时候,由于一个元素只能发出一条连接,我们查找结束。
接下来,我们定住b[j]按照同样的方法来在a[l]中去找,找到第一个a[l]==b[j]的地方,我们就结束掉。分别记录这两次查找的过程中a[l]==b[j]和a[i]==b[k]的l和k的位置,更新dp[i][j]用,因为但凡有了这两次连线,对于dp[i][j]来说就会贡献2条线。。。如果我们在两次查找的过程中,有某一次或者两次的l和k的位置都没有出现的话,我们就说明dp[i][j]并没有得到更新。
代码:
1 # include<cstdio> 2 # include<iostream> 3 # include<fstream> 4 # include<algorithm> 5 # include<functional> 6 # include<cstring> 7 # include<string> 8 # include<cstdlib> 9 # include<iomanip> 10 # include<numeric> 11 # include<cctype> 12 # include<cmath> 13 # include<ctime> 14 # include<queue> 15 # include<stack> 16 # include<list> 17 # include<set> 18 # include<map> 19 20 using namespace std; 21 22 const double PI=4.0*atan(1.0); 23 24 typedef long long LL; 25 typedef unsigned long long ULL; 26 27 # define inf 999999999 28 # define MAX 123 29 30 int a[MAX]; 31 int b[MAX]; 32 int dp[MAX][MAX]; 33 34 int main(void) 35 { 36 int t;cin>>t; 37 while ( t-- ) 38 { 39 int n,m; 40 cin>>n>>m; 41 for ( int i = 1;i <= n;i++ ) 42 cin>>a[i]; 43 for ( int i = 1;i <= m;i++ ) 44 cin>>b[i]; 45 int k,l; 46 for ( int i =1;i<= n;i++ ) 47 { 48 for ( int j = 1;j <= m;j++ ) 49 { 50 dp[i][j] = max( dp[i-1][j],dp[i][j-1] ); 51 if ( a[i]!=b[j] ) 52 { 53 for ( k = j-1;k >= 1;k-- ) 54 { 55 if ( b[k]==a[i] ) 56 break; 57 } 58 for ( l = i-1;l >= 1;l-- ) 59 { 60 if ( a[l]==b[j] ) 61 break; 62 } 63 64 if ( l!=0&&k!=0 ) 65 { 66 dp[i][j] = max(dp[i][j],dp[l-1][k-1]+2 ); 67 } 68 } 69 } 70 } 71 cout<<dp[n][m]<<endl; 72 73 } 74 75 76 return 0; 77 }