hdu5773 最长上升子序列变形(附带模板)

先说说最长上升子序列的模板,时间复杂度O(nlogn)

最长上升子序列
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 41000;
int a[N],p;       //a[i] 原始数据
int d[N];       //d[i] 长度为i的递增子序列的最小值

int BinSearch(int key, int low, int high)//二分查找,找不到则归0
{
    while(low<=high)
    {
        int mid = (low+high)>>1;
        if(key>d[mid] && key<=d[mid+1])
            return mid;
        else if(key>d[mid])
            low = mid+1;
        else
            high = mid-1;
    }
    return 0;
}

int LIS()
{
    int i,j;
    d[1] = a[1];
    int len = 1;        //递增子序列长度
    for(i = 2; i <= p; i++)
    {
        if(d[len]<a[i])
            j = ++len;
        else
            j = BinSearch(a[i],1,len) + 1;
        d[j] = a[i];
    }
    return len;
}

int main()
{
  //  freopen("in.txt","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&p);
        for(int i = 1; i <= p; i++)
            scanf("%d",&a[i]);//输入的数组
        printf("%d\n",LIS());
    }
    return 0;
}

dp[i]表示的是到目前为止,当子序列的长度为i时,对应数组a[]中的最小值,用len记录到目前为止可以组成的子序列的最长长度

当处理一个新的a[u]时,a[u]>dp[len]则直接dp[len+1]=a[u];

当a[u]<=dp[len]容易看出dp是一个严格单调递增的数组,则直接二分求解a[u]可以对dp的哪个元素进行更新

这题的题意是:

给你一堆乱序序列包含0,0可以变成任何整数包括负数,问最长上升子序列的长度

自己乱搞是在LIS模板的基础上碰到0就去更新dp数组每一个元素的值,若0在队首就将dp[1]=-1e9+7;

没想到居然过了大概数据比较水

#include <iostream>
#include <map>
#include <math.h>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <set>
#include<stdio.h>

using namespace std;
const int N=1e5+50;
const int M=1e9+7;
int t,n,a[N],dp[N];
int erfen(int l,int r,int u){
while(l<=r){
    int mid=(l+r)>>1;
    if(dp[mid]<a[u]&&dp[mid+1]>=a[u])
        return mid;
    else if(dp[mid]>a[u])
        r=mid-1;
    else
        l=mid+1;
}
    return 0;
}
int main()
{
   // freopen("in.txt","r",stdin);
    cin>>t;
    int cas=0;
    while(t--){
        cas++;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
            dp[i]=1000000+10;
        dp[0]=0;
        int i,j;
        for(i = 1;i <= n;i ++)
            scanf("%d",&a[i]);
        int len=0;
        dp[1]=min(dp[1],a[1]);
        len++;
       // cout<<dp[1]<<" "<<a[1]<<endl;
        for(i = 2;i <= n;i ++){
            //cout<<"r"<<endl;
            if(a[i]!=0){
                if(a[i]>dp[len])
                    j=++len;
                else
                    j=erfen(1,len,i)+1;
                dp[j]=a[i];
        }
            else{
                dp[len+1]=dp[len]+1;
                for(j=len;j>1;j--)
                    dp[j]=dp[j-1]+1;
                dp[1]=0;
                len++;
            }
    }
   printf("Case #%d: %d\n",cas,len);
    }
    return 0;
}

题解是:

0可以转化成任意整数,包括负数,显然求LIS时尽量把0都放进去必定是正确的,因为0放不进去的原因无非就是非严格递增。为了保证严格递增,我们可以将每个权值S[i]减去i前面0的个数,再做LIS,就能保证结果是严格递增的。因此我们可以把0拿出来,对剩下的做O(nlogn)的LIS,统计结果的时候再算上0的数量。只能说是膜拜。当然有点小细节,自己注意一下

#include <iostream>
#include <map>
#include <math.h>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <set>
#include<stdio.h>

using namespace std;
const int N=1e5+50;
const int M=1e9+7;
int t,n,a[N],dp[N],num[N];
int erfen(int l,int r,int u){
while(l<=r){
    int mid=(l+r)>>1;
    if(dp[mid]<a[u]&&dp[mid+1]>=a[u])
        return mid;
    else if(dp[mid]<a[u])
        l=mid+1;
    else
        r=mid-1;
}
    return 0;
}
int main()
{
  //  freopen("in.txt","r",stdin);
    cin>>t;
    int cas=0;
    while(t--){
        int cnt0=0;
        cas++;
       scanf("%d",&n);
       for(int i=1;i<=n;i++)
            dp[i]=1000000+10,num[i]=0;
        dp[0]=0,num[0]=0;
        int i,j,cnt=0;
       for(i = 1;i <= n;i ++){
            scanf("%d",&a[i]);
            if(a[i]==0)
                cnt++,cnt0++;
            if(a[i]!=0)
            num[i]=cnt;
       }
        for(i=1,cnt=0;i<=n;i++){
            if(a[i]!=0){
            a[++cnt]=a[i]-num[i];
            //cout<<a[cnt]<<" "<<cnt<<endl;
            }
        //cout<<a[cnt]<<" "<<cnt<<endl;
        }

        int len=0;
        dp[1]=min(dp[1],a[1]);
        len++;
       if(cnt==0)
        len=0;
       // cout<<dp[1]<<" "<<a[1]<<endl;
        for(i = 2;i <= cnt;i ++){
                if(a[i]>dp[len])
                    j=++len;
                else
                    j=erfen(1,len,i)+1;
                dp[j]=a[i];
    //cout<<j<<" "<<dp[j]<<endl;
    }

   printf("Case #%d: %d\n",cas,len+cnt0);
    }
    return 0;
}
时间: 2024-07-28 22:35:19

hdu5773 最长上升子序列变形(附带模板)的相关文章

HDU 1160 FatMouse&#39;s Speed 动态规划 记录路径的最长上升子序列变形

题目大意:输入数据直到文件结束,每行两个数据 体重M 和 速度V,将其排列得到一个序列,要求为:体重越大 速度越低(相等则不符合条件).求这种序列最长的长度,并输出路径.答案不唯一,输出任意一种就好了. 题目思路:这是个最长上升子序列的问题,我们按W的升序进行排序,若W相等则按V的降序排序.用Pre[]记录当前点的前驱节点,Last记录序列最后一个点,maxn记录最长长度,完成动规后可根据Last和Pre[]输出路径. #include<cstdio> #include<stdio.h&

最长公共子序列-jobdu-1402(附带打印--undo)

jobdu-1042:Coincidence 时间限制:1 秒 内存限制:32 兆 题目描述: Find a longest common subsequence of two strings. 输入: First and second line of each input case contain two strings of lowercase character a-z. There are no spaces before, inside or after the strings. Le

vijos P1028 最长上升子序列变形题

#include<iostream> #include<cstdio> #include<cstring> #include<map> #include<string> #include<vector> #include<algorithm> using namespace std; bool judge(const string &aa,const string &bb) { for(int i=0;i&

【HDU】5256 序列变换(最长上升子序列变形)

如果a[i]和a[j]想不变,需要满足的条件就是 a[j] - a[i] > j - i 也就是a[i] - i < a[j] - j 比如1 4 2 就不满足,所以1和2之间一定有一个需要改变 所以我们对所有a[i] - i求其最长上升子序列就可以了 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 100005; con

poj1159--Palindrome(dp:最长公共子序列变形 + 滚动数组)

Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 53414   Accepted: 18449 Description A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a

UVA 10723--Cyborg Genes+最长公共子序列变形

题目链接:点击进入 首先对于长度最短的情况是很容易确定的,只需要用两个字符串的长度和减去他们的最长公共子序列长度.然后比较麻烦的就是合乎要求的字符串的个数,其实我们也可以用类似于最长公共子序列的dp来求. 设dp[i][j]表示str1的前i个字符和str2的前j个字符所得到的满足要求的字符串,则如果str[i]==str[j],则dp[i][j]+=dp[i-1][j-1]; 否则就要根据i,j这两个位置上的最长公共子序列长度进行讨论,具体见代码. 代码如下: #include<iostrea

hdu1243(最长公共子序列变形)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1243 分析:dp[i][j]表示前i个子弹去炸前j个恐怖分子得到的最大分.其实就是最长公共子序列加每个字母值为1,这里每个字母代表的值变化了一下. 状态转移方程:if(s1[i-1]==s2[j-1])dp[nxt][j]=dp[cur][j-1]+val[s1[i-1]];                              else  dp[nxt][j]=max(dp[nxt][j-1]

poj1836--Alignment(dp,最长上升子序列变形)

Alignment Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 13319   Accepted: 4282 Description In the army, a platoon is composed by n soldiers. During the morning inspection, the soldiers are aligned in a straight line in front of the cap

51Nod 1092 回文字符串 | 最长公共子序列变形

求字符串和其逆的最长公共子序列,需要添加的字符数就为长度-最长公共子序列长 #include "stdio.h" #include "string.h" #define maxn 1005 char s[maxn],s1[maxn]; int dp[maxn][maxn]; int main() { int n=0,i,j,len; scanf("%s",s); len=strlen(s); strcpy(s1,s); strrev(s1); f