欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

poj 1631 Bridging signals

程序员文章站 2022-03-16 17:31:21
...

题目链接点击打开链接

Bridging signals
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 14521   Accepted: 7850

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? 
poj 1631 Bridging signals

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

Source


题意:求上升公共子序列


先介绍一种O(n*n)算法:

我们定义dp[i]: 以a[i]为末尾的最长上升子序列的长度

以a[i]结尾的上升子序列是

1 只包含a[i]的子序列

2 在满足j<i并且a[j]<a[i]的以a[j]为结尾的上升子序列末尾,追加上a[i]后得到的子序列

状态转移方程:dp[i]=max(dp[i],dp[j]+1)(当然该方程是有前提条件的)

然而由于数据过大,这样写会tle

tle 代码:


#include<iostream>
using namespace std;
int t;
int n;
int a[40000];
int dp[40000];
int ma;
int ans;
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        cin>>a[i];
        for(int i=1;i<=n;i++)
        dp[i]=1;
        ans=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
            if(a[j]<a[i])
            dp[i]=max(dp[i],dp[j]+1);
            }
            ans=max(ans,dp[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}

那我们要用O(logn)复杂度的算法来解决这道题

我们定义dp[i]:长度为i的上升子序列中末尾元素的最小值(不存在的话就是inf)

原理:如果子序列的长度相同,那么最末尾的元素较小的在之后会有优势。整个过程dp[i]是有序的,因此我们就可以采用二分查找来解决,那么时间复杂度就优化为nlogn。

我们举个例子来模拟一下整个过程;

假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。n
下面一步一步试着找出它。
我们定义一个序列B,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了

首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2

再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2

继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3

第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了

第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。

最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

那么使得dp[i]不为inf的最大的i即为最长上升子序列的长度(举例转自他人博客)


AC代码


//该题要用c语言不然会超时
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define inf 0x3f3f3f
using namespace std;
int t;
int n;
int a[40000];
int dp[40000];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
        scanf("%d",&a[i]);
        dp[i]=inf;//初始化为正无穷大
        }
        for(int i=1;i<=n;i++)
        {
            *lower_bound(dp+1,dp+1+n,a[i])=a[i];//lower_bound返回一个非递减序列[first, last)中的第一个大于等于值val的位置。因此我们加上*就是该位置所储存的变量
        }
        printf("%d\n",lower_bound(dp+1,dp+n+1,inf)-dp-1);//最后一个不为inf的数的下标即为答案
    }
}


最长上升子序列还有一种o(n*n)的方法是采用排序+最长公共子序列,在此就不在详述。