初入算法篇(动态规划)最长上升子序列poj2533+栈优化模板&&scau18090 好多好多球

Longest Ordered Subsequence
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 59128 Accepted: 26487

Description

A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence ( a1, a2, ..., aN) be any sequence ( ai1, ai2, ..., aiK), where 1 <= i1 < i2 < ... < iK <= N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).

Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.

Input

The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000

Output

Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.

Sample Input

7
1 7 3 5 9 4 8

Sample Output

4


最长上升子序列模板题,本质思想就是局部最优解推出全局最优解,由每一个点的前面,找出在这个点前的最长上升子序列,例如第六个元素,那么就找出前5个元素都能与它构成的子序列的最大值,转移方程为dp[i]=max(dp[i],dp[j]+1)  j<=i-1

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
int dp[100009];
int a[100009];
using namespace std;

int main()
{
    int M=0,i,j,n;
    scanf("%d",&n);
    for(i=0; i<=n-1; i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            if(a[i]>a[j])
            {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        M=max(M,dp[i]);
    }
    printf("%d\n",M);
}

时间复杂度O(N^2)需要优化,这里有栈优化代码,时间复杂度为O(nlogn)

栈优化中的二分查找,最后找出来的L值一定是比a[i]大,(或者等于,但是等于其实就是自己替换自己没有多大意义),这是二分的一个特点

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
int dp[100009];
int a[100009];
using namespace std;

/*int main()
{
    int M=0,i,j,n;
    scanf("%d",&n);
    for(i=0; i<=n-1; i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            if(a[i]>a[j])
            {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        M=max(M,dp[i]);
    }
    printf("%d\n",M);
}*/
/*int main()
{
    int n,i,j,M=0;
    while(~scanf("%d",&n))
    {
        M=0;
        for(i=0;i<=n-1;i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
    }
    for(i=0;i<=n-1;i++)
    {
        for(j=0;j<=i-1;j++)
        {
            if(a[i]==a[j]+1)
            {

               dp[i]=dp[j]+1;
            }
        }
        M=max(M,dp[i]);
    }
    printf("%d\n",n-M);
    }
}*/
//栈优化
int stack[10009];
int main()
{
    int top,n,i,j;
    scanf("%d",&n);
    for(i=0;i<=n-1;i++)
    {
        scanf("%d",&a[i]);
    }
    stack[0]=-9999;//有可能元素等于,这样保证一定会进栈
    top=0;//栈顶
    for(i=0;i<=n-1;i++)
    {
        if(a[i]>stack[top])
        {
            top++;
            stack[top]=a[i];
        }
        else //如果小于栈顶元素,就二分去找到它相应的位置,这样子可以最长上升子序列就可以保证“潜力”
        //但又保证了最长子序列的长度不会变化
        {
            int L=1;
            int R=top-1;
            while(L<=R)
            {
                int mid=(L+R)/2;
                if(a[i]>stack[mid]) L=mid+1;
                else R=mid-1;
            }
            stack[L]=a[i];//找到一个比a[i]大的第一个数
            //假如相等的话,会直接替换想等值,没有影响
        }

    }
    printf("%d\n",top);



}

接着是scau的题目

18090 好多好多球

时间限制:1000MS  内存限制:65535K
提交次数:329 通过次数:90 收入:70

题型: 编程题   语言: 不限定

Description

一天,Jason买了许多的小球。有n个那么多。他写完了作业之后就对着这些球发呆,这时候邻居家的小朋友ion回来了,
Jason无聊之际想到了一个游戏。他把这n个小球从1到n进行标号。然后打乱顺序,排成一排。然后让ion进行一种操作:
每次可以任意选择一个球,将其放到队列的最前端或者队列的最末尾。问至少要进行多少次操作才能使得球的顺序变成正序1,2,3,4,5……n。



输入格式

包含多组测试数据,每组数据第一行输入一个n(1 <= n <= 100),表示有n个球。第二行有n个数字ai(1 <= ai <= n),ai两两各不相同。


输出格式

每组测试数据输出占一行,表示最少的操作次数使得小球变得有序。


输入样例

4
3 2 1 4

2
2 1


输出样例

2
1


提示

听说有个东西叫最长上升子序列。
题目是多CASE输入,请用while (scanf("%d", &n) != EOF) {...}来读入n。

乍一看好像不是最长上升子序列,其实这就是找一个相差为1的最长上升子序列,例如数3 9 8 4 0 10 5 那么这个特定的最长上升子序列是不是就是 3 4 5 ,记住,这道题是特定的一点条件,改改代码将条件改为if(a[i]==a[j]+1)即可,其实后面取最大值的点我们也可以直接dp[i]=dp[j]+1因为这个连续数一定是唯一的,所以取不取最大都是可以的。找到最长上升子序列后,我们就可以把不属于这个序列的球放到队头或者队尾,拿上面的的例子,将0放最前面,然后依次8,9,10

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
int dp[100009];
int a[100009];
using namespace std;

/*int main()
{
    int M=0,i,j,n;
    scanf("%d",&n);
    for(i=0; i<=n-1; i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
    }
    for(i=0; i<=n-1; i++)
    {
        for(j=0; j<=i-1; j++)
        {
            if(a[i]>a[j])
            {
                dp[i]=max(dp[i],dp[j]+1);
            }
        }
        M=max(M,dp[i]);
    }
    printf("%d\n",M);
}*/
int main()
{
    int n,i,j,M=0;
    while(~scanf("%d",&n))
    {
        M=0;
        for(i=0;i<=n-1;i++)
    {
        scanf("%d",&a[i]);
        dp[i]=1;
    }
    for(i=0;i<=n-1;i++)
    {
        for(j=0;j<=i-1;j++)
        {
            if(a[i]==a[j]+1)
            {

               dp[i]=dp[j]+1;
            }
        }
        M=max(M,dp[i]);
    }
    printf("%d\n",n-M);
    }
}
最后要找的是要放的小球所以是n-M
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值