这一题很容易抽象成最长上升子序列问题:
最长上升子序列问题:
给出一个由n个数组成的序列x[1..n],找出它的最长单调上升子序列。即求最大的m和a1,
a2……,am,使得a1<a2<……<am且x[a1]<x[a2]<……<x[am]。
动态规划求解思路分析:(O(n^2))
经典的O(n^2)的动态规划算法,设A[i]表示序列中的第i个数,F[i]表示从1到i这一段中以i结尾的最长上升子序列的长度,初始时设F[i] = 0(i = 1, 2, ..., len(A))。则有动态规划方程:F[i] = max{1, F[j] + 1} (j = 1, 2, ..., i - 1, 且A[j] < A[i])。
贪心+二分查找:(O(nlogn))
开辟一个栈,每次取栈顶元素s和读到的元素a做比较,如果a>s, 则加入栈;如果a<s,则二分查找栈中的比a大的第1个数,并替换。 最后序列长度为栈的长度。
这也是很好理解的,对x和y,如果x<y且E[y]<E[x],用E[x]替换 E[y],此时的最长序列长度没有改变但序列Q的''潜力''增大。
举例:原序列为1,5,8,3,6,7
栈为1,5,8,此时读到3,则用3替换5,得到栈中元素为1,3,8, 再读6,用6替换8,得到1,3,6,再读7,得到最终栈为1,3,6,7 ,最长递增子序列为长度4。
用贪心+二分的方法很容易模拟出来,需要注意的就是二分的写法,而且需要输出最长上升子序列时,直接输出栈中的元素就行了。
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 const int MAXN = 500000 + 5; 5 int s[MAXN]; //s[]数组用来模拟栈 6 int binsearch(int *a, int l, int r, int val) 7 { 8 while (l <= r) 9 { 10 int mid = (l + r) >> 1; 11 if (val < a[mid]) 12 r = mid - 1; 13 else if (val > a[mid]) 14 l = mid + 1; 15 else 16 return mid; 17 } 18 return l; 19 } 20 int main() 21 { 22 int n, p, r, a[MAXN]; 23 s[0] = INT_MIN; 24 int icase = 0; 25 while (scanf("%d", &n) != EOF) 26 { 27 ++icase; 28 for (int i = 0; i < n; ++i) 29 { 30 scanf("%d %d", &p, &r); 31 a[p] = r; 32 } 33 int val, t = 1; //t标记数组最后一个元素的下一位置,起始为1 34 for (int i = 1; i <= n; ++i) 35 { 36 val = a[i]; 37 if (val > s[t-1]) 38 s[t++] = val; 39 else 40 s[binsearch(s, 1, t - 1, val)] = val; 41 } 42 if (t - 1 == 1) 43 printf("Case %d:\nMy king, at most %d road can be built.\n\n", icase, t - 1); 44 else 45 printf("Case %d:\nMy king, at most %d roads can be built.\n\n", icase, t - 1); 46 } 47 return 0; 48 }