题意:有一些穷国和一些富国分别排在两条直线上,每个穷国和一个富国之间可以建道路,但是路不能交叉,给出每个穷国和富国的联系,求最多能建多少条路
我一开始在想有点像二分图匹配orz,很快就发现,当我把穷国按顺序排了之后,富国写在它旁边,能够连接的富国就成了一个上升子序列,那么问题来了!上升子序列最长有多长?
想到了这个之后,代码就码起来吧,最开始我的做法是最土的那种,用 dp[i] 表示以 i 结尾的最长上升子序列的长度,每次对于一个 i 遍历 i 前面的所有数 j ,取小于 i 的所有 j 的最大 dp[j] ,在这个基础上加一就是以 i 结尾的最长上升子序列的长度了。
于是,就这么水过了此文终结```怎么可能!显然我TLE了!我考虑了各种简化循环的姿势,比如把循环放进读取里面啊这类的```然后```我以不同的姿势连T了三次T得不要不要的。
看了题解```dp+二分优化```你们可懂我的心情如同万千*奔过```我左思右想我的程序怎么加入二分法,可是明显并不能,仔细看题解之后才发现并不是我这么求的LIS。
这里的 dp 记录的是长度为 i 的上升子序列的最优结尾情况,也就是记录长度为 i 的上升子序列的结尾最短可以是多少。
模拟一下这个 dp 的方式:
例如我们现在有一个序列 10 1 3 2 4 7 9 5 6 8
首先起始是第一个数 10 ,那么我首先记录 dp[1]=10 ,这表示我当前状态下长度为 1 的子序列最小的结尾是 10 ,这个子序列就是 10;
接下来我遍历到第二个数 1 ,1 明显是小于 dp[1] 也就是 10 的,那么我就用 1 更新 dp[1] ,dp[1]=1 ,这表示遍历到第二个数 1 时,长度为 1 的子序列最小结尾是 1,这个子序列就是 1;
然后我遍历第三个数 3 ,3 大于 dp[1] 也就是 3 大于 1,那么我就记录 dp[2]=3 ,这表示此时长度为 2 的子序列最小结尾是 3,而这个子序列就是 1 3;
第四个数 2, 2 小于 dp[2] ,那我就在前面的值里找第一个比 2 大的值将它替代,dp[1]=1<2 ,dp[2]=3>2 ,于是我用 2 更新 dp[2]=2 ;表示此时长度为 2 的上升子序列最小结尾是 2 ,这个子序列就是 1 2;
第五个数 4 , 4 大于 dp[2] ,那么我就记录 dp[3]=4 ,表示长度 3 的子序列最小结尾是 4 ,这个子序列是 1 2 4;
第六个数 7 , 7 大于 dp[3] ,记录 dp[4]=7 ,代表子序列 1 2 4 7;
第七个数 9 , 9 大于 dp[4] ,记录 dp[5]=9 ,代表子序列 1 2 4 7 9;
第八个数 5 , 5 小于 dp[5] , 那么在 dp 数组中找第一个比 5 大的数优化它,寻找到了 dp[4]=7 ,那么就改为 dp[4]=5 ,代表此时长度为 4 的子序列最小结尾为 5 ,即子序列 1 2 4 5 ;
第九个数 6 , 6 小于 dp[5] ,那么在 dp 数组中找第一个比 6 大的数优化它,寻找到了 dp[5]=9 ,那么改为 dp[5]=6 ;代表此时长度为 5 的子序列最小结尾为 6 ,1 2 4 5 6;
最后一个数 8 , 8 大于 dp{5] ,那么直接记录 dp[6]=8 ,代表子序列 1 2 4 5 6 8;
这样整个最长上升子序列就出现了 1 2 4 5 6 8 ,长度为 6 ,在这种算法中,当数列非常长的时候,在前面所有的 dp 数组中寻找首个比当前数大的数的位置就非常耗时,因此即使仅仅 dp 是会 TLE 的,由于 dp 数组无论怎么更新都是保证上升的,因此我们就可以将寻找的工作加入二分查找的思想,这样就能保证数据大的情况下不会超时了。
恩```基本就是这样,但是我显然在 T 了三次之后又 WA 了几次,坑爹的事实粗线了,大概没有人会像我这么蠢得以为它给的时候穷国的顺序一定是从 1 开始一直上升的所以根本就不用读第一个数只要读它对应的富国就好了吧```我已经感受到了世界对我深深的恶意```代码里那绿绿的一段就是我当初觉得一定是按顺序的就可以把 dp 过程直接放入读数据循环来减少循环了```
1 #include<stdio.h>
2 #include<string.h>
3 int g[500011],dp[500011]; 4 int main(){ 5 int n; 6 int ti=0; 7 while(scanf("%d",&n)!=EOF){ 8 memset(g,0,sizeof(g)); 9 memset(dp,0,sizeof(dp)); 10 int ans=0; 11 for(int p=1;p<=n;p++){ 12 int t,l; 13 scanf("%d%d",&t,&l); 14 g[t]=l; 15 } 16 dp[++ans]=g[1]; 17 for(int i=2;i<=n;i++){ 18 int low,high,mid; 19 low=1;high=ans; 20 while(low<=high){ 21 mid=(low+high)/2; 22 if(dp[mid]<g[i])low=mid+1; 23 else high=mid-1; 24 } 25 dp[low]=g[i]; 26 if(low>ans)ans++; 27 } 28 printf("Case %d:\n",++ti); 29 if(ans==1) printf("My king, at most 1 road can be built.\n"); 30 else printf("My king, at most %d roads can be built.\n",ans); 31 printf("\n"); 32 } 33 return 0; 34 }