《算法竞赛-训练指南》第一章-1.27-UVa 10635

来源:互联网 发布:银行家算法实验报告 编辑:程序博客网 时间:2024/06/08 01:49

这是道很好的题目,看起来很水,但是确实做出来还是需要你见多识广,看的题目多了,然后自己做的题目多了才有可能做出来。


题目的描述是这样的,很简单,就是两个数组求最长公共子序列,LCS,但是范围有点大了62500,如果用dp的做法,O(mn)肯定会超时的。题目中的任何一个信息都是非常有用的,比如这里,题目中说明了,不走重复的路,所以也就代表了,两个数组中不会出现重复的元素。题解还是非常巧妙的,将第一个数组重标号,从1到N,那么只要对第二组做一个LIS就可以求出最终的答案了。


由于LIS也是自己手写的,其中的binarySearch也是自己手写的,所以写的比较慢,一会常识一下用STL的lower_bound(),看时间怎么样。另外自己也把dp版本的最长公共子序列贴出来吧。


dp:

#include <stdio.h>#include <string.h>#include <iostream>#include <string>using namespace std;const int MAXN = 100;int N;int M;int A[MAXN];int B[MAXN];int d[MAXN][MAXN];int main(){while (scanf("%d%d", &N, &M) != EOF){for (int i = 1; i <= N; i++){scanf("%d", &A[i]);}for (int j = 1; j <= M; j++){scanf("%d", &B[j]);}d[0][0] = 0;for (int i = 1; i <= N; i++){for (int j = 1; j <= M; j++){if (A[i] == B[j]){d[i][j] = d[i - 1][j - 1] + 1;}else{d[i][j] = max(d[i - 1][j], d[i][j - 1]);}}}printf("%d\n", d[N][M]);}system("pause");return 0;}



此题的解法:

#include <stdio.h>#include <string.h>#include <iostream>#include <string>using namespace std;const int MAXN = 62500 + 11;int n, p, q;int hash[MAXN];int A[MAXN];int C[MAXN];int binary_Search(int l, int r, int key){int L = l;int R = r;while (L < R){int mid = L + (R - L + 1) / 2;if (C[mid] < key){L = mid;}else{R = mid - 1;}}return L + 1;}int LIS(int *B, int N){int len = 0;C[len] = B[0];for (int i = 1; i < N; i++){if (C[len] < B[i]){C[++len] = B[i];}else{int t = binary_Search(0, len, B[i]);C[t] = B[i];}}return len + 1;}int main(){int T;scanf("%d", &T);for (int Case = 1; Case <= T; Case++){int a;memset(hash, 0, sizeof(hash));scanf("%d%d%d", &n, &p, &q);for (int i = 1; i <= p + 1; i++){scanf("%d", &a);hash[a] = i;}int cnt = 0;for (int i = 0; i < q + 1; i++){scanf("%d", &a);if (hash[a]){A[cnt++] = hash[a];}}int ans = LIS(A, cnt);printf("Case %d: %d\n", Case, ans);}//system("pause");return 0;}

最后加上自己对lower_bound,upper_bound的理解,其实一般情况下,只用lower_bound就可以解决问题。而好像对于LIS,却要视情况而定用lower_bound还是upper_bound,对于不同的数,两者插入是一样的,都是刚好key在的那个位置,但是如果已经存在一个和key相同的值的时候,这个时候upper——bound就返回的是下一个值,而lower_bound返回的确实此值,所以要按情况分析用lower还是upper,当然,显而易见,严格递增的要用lower,非递减的用upper,这道题目都可以,易得.贴出代码:

#include <stdio.h>#include <string.h>#include <iostream>#include <string>using namespace std;int A[111];int main(){int N;while (scanf("%d", &N) != EOF){for (int i = 0; i < N; i++){scanf("%d", &A[i]);}int t1 = lower_bound(A, A + N, 3) - A;int t2 = upper_bound(A, A + N, 3) - A;cout << "t1 = " << t1 << endl;cout << "t2 = " << t2 << endl;} system("pause");return 0;}/*结论:当参数 key 没有在容器key的范围内:1. key小于容器所有的数 uper_bound,lower_bound 都将返回 begin.2. key大于容器所有的数 uper_bound, lower_bound 都将返回 end当参数key 在容器key 范围内:1. 参数key == 容器数. lower_bound 将返回当前key 的iterator, uper_bound 将返回下一个元素的iterator.2. 参数key 不等于 容器数,且在范围内, lower_bound将返回比参数key大的且相邻的容器数的iterator,upper一样; 3. 如果Key等于begin或等于end,将返回begin 或end */




#include <stdio.h>#include <string.h>#include <iostream>#include <string>#include <algorithm>using namespace std;const int MAXN = 62500 + 11;int n, p, q;int hash[MAXN];int A[MAXN];int C[MAXN];int binary_Search(int l, int r, int key){int L = l;int R = r;while (L < R){int mid = L + (R - L + 1) / 2;if (C[mid] < key){L = mid;}else{R = mid - 1;}}return L + 1;}int LIS(int *B, int N){int len = 0;C[len] = B[0];for (int i = 1; i < N; i++){if (C[len] < B[i]){C[++len] = B[i];}else{//int t = binary_Search(0, len, B[i]);int t = lower_bound(C, C + len, B[i]) - C;//cout << "t = " << t << endl;C[t] = B[i];}}return len + 1;}int main(){int T;scanf("%d", &T);for (int Case = 1; Case <= T; Case++){int a;memset(hash, 0, sizeof(hash));scanf("%d%d%d", &n, &p, &q);for (int i = 1; i <= p + 1; i++){scanf("%d", &a);hash[a] = i;}int cnt = 0;for (int i = 0; i < q + 1; i++){scanf("%d", &a);if (hash[a]){A[cnt++] = hash[a];}}int ans = LIS(A, cnt);printf("Case %d: %d\n", Case, ans);}//system("pause");return 0;}



原创粉丝点击