Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 59128 | Accepted: 26487 |
Description
Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.
Input
Output
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