Bridging signals
Description
'Oh no, they've done it again', cries the chief designer at the Waferland chip factory. Once more the routing designers have screwed up completely, making the signals on the chip connecting the ports of two functional blocks cross each other all over the place. At this late stage of the process, it is too expensive to redo the routing. Instead, the engineers have to bridge the signals, using the third dimension, so that no two signals cross. However, bridging is a complicated operation, and thus it is desirable to bridge as few signals as possible. The call for a computer program that finds the maximum number of signals which may be connected on the silicon surface without crossing each other, is imminent. Bearing in mind that there may be thousands of signal ports at the boundary of a functional block, the problem asks quite a lot of the programmer. Are you up to the task?
A typical situation is schematically depicted in figure 1. The ports of the two functional blocks are numbered from 1 to p, from top to bottom. The signal mapping is described by a permutation of the numbers 1 to p in the form of a list of p unique numbers in the range 1 to p, in which the i:th number specifies which port on the right side should be connected to the i:th port on the left side.Two signals cross if and only if the straight lines connecting the two ports of each pair do.
A typical situation is schematically depicted in figure 1. The ports of the two functional blocks are numbered from 1 to p, from top to bottom. The signal mapping is described by a permutation of the numbers 1 to p in the form of a list of p unique numbers in the range 1 to p, in which the i:th number specifies which port on the right side should be connected to the i:th port on the left side.Two signals cross if and only if the straight lines connecting the two ports of each pair do.
Input
On the first line of the input, there is a single positive integer n, telling the number of test scenarios to follow. Each test scenario begins with a line containing a single positive integer p < 40000, the number of ports on the two functional blocks. Then follow p lines, describing the signal mapping:On the i:th line is the port number of the block on the right side which should be connected to the i:th port of the block on the left side.
Output
For each test scenario, output one line containing the maximum number of signals which may be routed on the silicon surface without crossing each other.
Sample Input
4 6 4 2 6 3 1 5 10 2 3 4 5 6 7 8 9 10 1 8 8 7 6 5 4 3 2 1 9 5 8 9 2 3 1 7 4 6
Sample Output
3 9 1 4
题目大意:求最长上升子序列,序列长度最大为40000。
分析:如果用一般的LIS算法,时间复杂度高达n^2。这里引用《入门经典》复杂度为O(nlogn)的方法。
假设已经计算出的两个状态 a 和 b 满足Aa < Bb 且d(a)==d(b),则对于后续所有状态 i(即i>a且i>b)来说,a并不会比b差——如果b满足Ab < Ai的条件,a也满足,且二者的d值相同;但反过来却不一定了。换句话说,如果我们只保留a,一定不会丢失最优解。
这样,对于相同的d值,只需要保留A最小的一个。我们用g(i)表示d值为i的最小状态编号。根据上述推理证明
g(1)<=g(2)<=g(3)<=...<=g(n)
上述的g值是动态改变的。对于一个给定的状态i,我们只考虑在i之前已经计算过的状态j(即j<i)。在给定状态i时可以用二分查找得到满足g(k)>=Ai的第一个下标k,则d(i)=k,此时Ai<g(k),而d(i)=k,所以更新g(k)=Ai。(话说看的不是很明白)
for(i=1; i<=n; i++) g[i] = INF;
for(i=0; i<n; i++)
{
int k = lower_bound(g+1,g+n+1,A[i]) - g;
d[i]=k;
g[k] = A[i];
}
代码如下:
1 # include<cstdio> 2 # include<iostream> 3 # include<algorithm> 4 using namespace std; 5 # define INF 0xffffff 6 int n; 7 int g[40005],A[40005]; 8 9 int main() 10 { 11 int i,T; 12 scanf("%d",&T); 13 while(T--) 14 { 15 scanf("%d",&n); 16 for(i=0; i<n; i++) 17 scanf("%d",&A[i]); 18 int ans = 0; 19 for(i=1; i<=n; i++) g[i] = INF; 20 for(i=0; i<n; i++) 21 { 22 int k = lower_bound(g+1,g+n+1,A[i]) - g; 23 g[k] = A[i]; 24 if(k>ans) 25 ans = k; 26 } 27 printf("%d ",ans); 28 } 29 return 0; 30 }
LIS nlogn算法大罗列!
网上有这一方面的总结 //n是原序列长度,a[]是原序列,D是a[]的值域大小
1. f[i]表示a[i]结尾的LIS长度,f[i] = max{f[j]}+1 : a[j]<a[i]
1.1 维护一个以a的值为下标,以f的值为值的树状数组优化转移。O(n log D)
1.2 g[x]表示长度为x的所有LIS中最小的末尾的值,可证g[x]单调递增,二分查找转移。O(n log n)
1.3 维护一个“最优”的LIS q,每次将q关于a[i]的lower_bound更新为a[i],同时转移。O(n log n)
1.2代码如下:
1 #include <iostream> 2 using namespace std; 3 4 int a[40001]; 5 int dp[40001]; 6 int b[40001], blen; 7 int n; 8 9 int main() { 10 int ca,i; 11 scanf("%d", &ca); 12 while (ca--) { 13 scanf("%d", &n); 14 for (i = 1; i <= n; ++i) { 15 scanf("%d", a+i); 16 } 17 memset(b,0,sizeof(b)); 18 memset(dp,0,sizeof(dp)); 19 20 21 int left, right, mid; 22 blen = 0; 23 int res = 0; 24 for (i = 1; i <= n; ++i) { 25 left = 1; 26 right = blen; 27 int num = a[i]; 28 while (left <= right) { 29 mid = (left + right)/2; 30 if (b[mid] < a[i]) { 31 left = mid + 1; 32 } 33 else { 34 right = mid - 1; 35 } 36 } 37 dp[i] = left; 38 b[left] = a[i]; 39 if (blen < left) 40 blen = left; 41 if (res < dp[i]) 42 res = dp[i]; 43 } 44 printf("%d ", res); 45 } 46 return 0; 47 }