POJ1631

题意:

在电路板的一侧有1 2 3 ...n n个端口 另一侧亦然 每个左侧的端口通过一条线和右边的某条端口连接起来

电路板设计时考虑不周 因此有些连线交叉 现在需要保留一些电线在电路板上(保留下来的电路板上的连线不能有交叉) 其他的采用飞线解决(焊板子的伤不起。。)

求能留在电路板上的电线的数量的最大值

 

思路:

将左边的端口号视为数组的index 右边与其相连的端口号视为数组里的值 则问题实质就是求该数组的最长不降子序列

 

求不降子序列的动态规划方法:

设有数组a[i],0<=i<=n, 如果能从i=0开始 逐渐求出以a[i]结尾的最长的不降子序列的长度,并将其存在一个数组dp[i]中

则在计算以a[i+1]结尾的最长不降子序列的长度时,可以遍历数组dp[j=0..i]  如果dp[j]+1 的值比当前dp[i+1]的值大 而且a[j]<a[i]

那么说明将a[i+1]加到以a[j]结尾的最长不降子序列之后所得到的序列 是新的最长的以a[i+1]结尾的最长不降子序列 所以此时把dp[i+1]置为dp[j]+1

这样得到的最后的数组dp[i]即代表以a[i]结尾的最长不降子序列的长度 dp[i]中的最大值即为所求a[i]的最长不降子序列的长度

时间复杂度: 需要遍历数组a[i] 对于每个a[i]又需要遍历dp[0..i-1] ~ 完成之后,需要遍历数组dp 所以O(1+2+3+..n-1)+O(n)=O(n^2)  空间复杂度O(n)

 

但是按这种算法提交TLE 研究了一下相关文章介绍的O(nlogn)的算法:

另外开辟一个数组array[], array[x]=y 的含义是 存在a[i]=y 而且在a中 若有1个以上的长度为x的不降子序列 则在对于所有这些子序列的最后一个数字 x是最小值

那么在考虑a[i]时 只需在array[]中查找与a[i]相等或者小于a[i]但与他最接近的元素 设为array[t]=a[k] 那么显然把a[i]接在a[k]后面,得到的就是以a[i]结尾的最长不降子序列

该子序列的长度为[t+1] 将其存入dp[i]中 并观察当前的array[t+1]与a[i]的大小关系 如果a[i]较小则用a[i]替换之(按照array的定义) 

array是一个有序数组(非降 原因是:设有array[a]=b,array[c]=d,a<c,b>d,  意味着长度为c的不降子序列中 尾部数字最小的是d 则显然该序列中所有元素都不会大于d 那么这些元素都小于b 在这些元素(一共c个 包括d)中选出a个 组成新的子序列 则该序列是以一个有a个元素 并以一个小于b的元素结尾的不降子序列, which, 根据array[a]=b是不可能的)

由于array非降 则在array中查找可以使用二分查找 时间复杂度降为O(nlogn) AC~

代码:

 1 // 1631.cpp : Defines the entry point for the console application.
 2 //This code is A MESS!
 3 
 4 #include "stdio.h"
 5 
 6 unsigned int biSearch(int data[],unsigned int length,int num) 
 7 {
 8     int left=0,right=length,mid=(left+right)/2;            
 9     while(left<=right)
10     {
11         if(num>data[mid]) left=mid+1;
12         else if(num<data[mid]) right=mid-1;
13         else return mid;
14         mid=(left+right)/2;
15     }
16     return right;
17 
18 }
19 
20 unsigned int solve(unsigned int data[],int length)            //find the length of Longest Increasing Sublist
21 {
22     unsigned int i;
23     int j;
24     unsigned int ans=0;
25     unsigned int temp[40005];
26     int temp2[40005];
27 
28     for(i=0;i<length;i++){
29         temp[i]=1;
30         temp2[i]=40006;
31     }
32     temp2[0]=-1;
33     temp2[1]=data[0];
34 
35 
36     for(i=1;i<length;i++)
37     {
38         j=biSearch(temp2,i,data[i]);
39         temp[i]=j+1;
40         if(data[i]<temp2[j+1])
41             temp2[j+1]=data[i];
42     }
43 
44 
45 
46     for(i=0;i<length;i++)
47         if(ans<temp[i]) ans=temp[i];
48     return ans;
49 }
50 
51 
52 
53 int main(void)
54 {
55     unsigned int scenarios;
56     unsigned int ports,portCount;
57     unsigned int sceCount=0;
58     unsigned int data[40005];
59 
60 
61     scanf("%d",&scenarios);
62     while(scenarios!=0)
63     {
64         scanf("%d",&ports);
65         for(portCount=0;portCount<ports;portCount++)
66             scanf("%d",&data[portCount]);                //deposit data of one scenario in array data
67         printf("%d",solve(data,ports));
68         scenarios--;
69     }
70 
71     return 0;
72 }
posted on 2012-09-03 13:44 ljwcfan 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/ljwcfan/archive/2012/09/03/2668670.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值