hdu1025 dp(最长上升子序列LIS)

题意:有一些穷国和一些富国分别排在两条直线上,每个穷国和一个富国之间可以建道路,但是路不能交叉,给出每个穷国和富国的联系,求最多能建多少条路

我一开始在想有点像二分图匹配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 过程直接放入读数据循环来减少循环了```

#include<stdio.h>
#include<string.h>
int g[500011],dp[500011];
int main(){
    int n;
    int ti=0;
    while(scanf("%d",&n)!=EOF){
        memset(g,0,sizeof(g));
        memset(dp,0,sizeof(dp));
        int ans=0;
        for(int p=1;p<=n;p++){
            int t,l;
            scanf("%d%d",&t,&l);
            g[t]=l;
        /*    if(p==1){
                dp[++ans]=g[p];
            }
            else {
                int low,high,mid;
                low=1;
                high=ans;
                while(low<=high){
                    mid=(low+high)/2;
                    if(dp[mid]<g[p])low=mid+1;
                    else high=mid-1;
                }
                dp[low]=g[p];
                if(low>ans)ans++;
            }*/
        }
        dp[++ans]=g[1];
        for(int i=2;i<=n;i++){
            int low,high,mid;
            low=1;high=ans;
            while(low<=high){
                mid=(low+high)/2;
                if(dp[mid]<g[i])low=mid+1;
                else high=mid-1;
            }
            dp[low]=g[i];
            if(low>ans)ans++;
        }
        printf("Case %d:\n",++ti);
        if(ans==1) printf("My king, at most 1 road can be built.\n");
        else printf("My king, at most %d roads can be built.\n",ans);
        printf("\n");
    }
    return 0;
}
时间: 2024-10-26 18:45:18

hdu1025 dp(最长上升子序列LIS)的相关文章

poj1836——dp,最长上升子序列(lis)

poj1836——dp,最长上升子序列(lis) Alignment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13767   Accepted: 4450 Description In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight

动态规划(DP),最长递增子序列(LIS)

题目链接:http://poj.org/problem?id=2533 解题报告: 状态转移方程: dp[i]表示以a[i]为结尾的LIS长度 状态转移方程: dp[0]=1; dp[i]=max(dp[k])+1,(k<i),(a[k]<a[i]) #include <stdio.h> #define MAX 1005 int a[MAX];///存数据 int dp[MAX];///dp[i]表示以a[i]为结尾的最长递增子序列(LIS)的长度 int main() { int

最长上升子序列LIS解法(n^n &amp;&amp; nlogn)

最长递增子序列问题 在一列数中寻找一些数满足 任意两个数a[i]和a[j] 若i<j 必有a[i]<a[j] 这样最长的子序列称为最长递增子序列LIS LIS问题有两种常见的解法 一种时间复杂度n^n 一种时间复杂度nlogn 下面我们先来说一下n^n的算法 设dp[i]表示以i结尾的最长上升子序列的长度 把问题分解 分解成序列中每一项最为终点的最大上升子序列 从第二项开始依次判断 最后找出最大的一项就是答案 则状态转移方程为 dp[i] = max{dp[j]+1}, 1<=j<

HDU 1087 &amp;&amp; POJ 2533(DP,最长上升子序列).

~~~~ 两道题的意思差不多,HDU上是求最长上升子序列的和,而POJ上就的是其长度. 貌似还有用二分写的nlogn的算法,不过这俩题n^2就可以过嘛.. ~~~~ 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1087 http://poj.org/problem?id=2533 ~~~~ HDU1087: #include<cstdio> #include<cstring> #include<algorithm> #

POJ 2250 Compromise (DP,最长公共子序列)

Compromise Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6440 Accepted: 2882 Special Judge Description In a few months the European Currency Union will become a reality. However, to join the club, the Maastricht criteria must be fulfille

最长上升子序列LIS模板

1 ///最长上升子序列LIS模板 2 int BinSerch(int l,int r,int cut) 3 { 4 while (l<=r) 5 { 6 int m=(l+r)>>1; 7 if (cut>d[m]&&cut<=d[m+1]) return m; 8 if (cut>d[m]) l=m+1; 9 else r=m-1; 10 } 11 return 0; 12 } 13 14 int LIS(int n) 15 { 16 int le

codeforces340D - Bubble Sort Graph dp + 最长上升子序列

题意:给你长为n的序列 ,每个节点都和在它前面且值比他大的点产生一条边,问你一个最大 两两点没有边的集合的 集合元素有多少 解题思路:想了半天才发现是最长上升子序列.. 解题代码: 1 // File Name: 340d.cpp 2 // Author: darkdream 3 // Created Time: 2014年08月03日 星期日 12时31分24秒 4 5 #include<vector> 6 #include<list> 7 #include<map>

hdu 1025 dp 最长上升子序列

1 //Accepted 4372 KB 140 ms 2 //dp 最长上升子序列 nlogn 3 #include <cstdio> 4 #include <cstring> 5 #include <iostream> 6 using namespace std; 7 const int imax_n = 500005; 8 int dp[imax_n]; 9 int d[imax_n]; 10 int a[imax_n]; 11 int n; 12 int len

UVA 10131 Is Bigger Smarter?(DP最长上升子序列)

Description Question 1: Is Bigger Smarter? The Problem Some people think that the bigger an elephant is, the smarter it is. To disprove this, you want to take the data on a collection of elephants and put as large a subset of this data as possible in