一、最长上升子序列+输出路径
最长上升子序列
题目描述
给定一个整数序列A1A2A3….An。求它的一个递增子序列,使子序列的元素个数尽量多,元素不一定要求连续。
输入
第1行:1个整数n(1<=n<=5000),表示序列中元素的个数.
第2行-n+1行:每行1个整数x(-1000<=x<=1000),第i+1行表示序列中的第i个元素。
输出
第1行:1个整数k,表示最长上升子序列的长度。
第2行:k个用单个空格分开的整数,表示找到了最长上升子序列。如果有多个长度等于k的子序列,则输出最靠前的1个。
样例输入
8
1
3
2
4
3
5
4
6
样例输出
5
1 3 4 5 6
如何处理第二问呢?
其实再用一个数组g[i]来存f[i]的前驱就可以了,输出的时候用一个循环找回去。
#include<cstdio>
int a[5005][3];//把f[i]和g[i]合并为一个数组
int main()
{
int n,i,j,l,k;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[i][0]);
a[i][1]=1;a[i][2]=0;
}
for(i=n-1;i>=1;i--){
l=0;k=0;
for(j=i+1;j<=n;j++){
if(a[j][0]>a[i][0]&&a[j][1]>l){
l=a[j][1];
k=j;
}
if(l>0){
a[i][1]=l+1;
a[i][2]=k;
}
}
}
k=1;
for(j=1;j<=n;j++)
if(a[j][1]>a[k][1]) k=j;
printf("%d\n",a[k][1]);
printf("%d",a[k][0]);
k=a[k][2];
while(k!=0){
printf(" %d",a[k][0]);
k=a[k][2];
}
}
拦截导弹
题目描述
输入
输出
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
389 207 155 300 299 170 158 65
样例输出
6
2
提示
第2问最直观的算法是贪心,但是反例也容易找到,如:
6 5 1 7 3 2
如果第一次打6 5 3 2,显然还要打两次,而最好的方案是6 5 1/7 3 2。
#include<cstdio>
int a[5005],b[5005],h[5005];
int main()
{
int i,j,n,m,x,s;
i=1;n=0;m=0;
while(scanf("%d",&a[i])==1){
s=0;
for(j=1;j<i;j++)
if(a[j]>=a[i]&&b[j]>s)
s=b[j];
b[i]=s+1;
if(b[i]>m) m=b[i];
x=0;
for(j=1;j<=n;j++)
if(h[j]>=a[i]){
if(x==0) x=j;
else if(h[j]<h[x]) x=j;
}
if(x==0){n++;x=n;}
h[x]=a[i];
i++;
}
printf("%d\n%d",m,n);
}
三、最长上升子序列*2
合唱队形
题目描述
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足存在i(1≤ i ≤K)使得Ti <T2< ......< Ti-1 < Ti >Ti+1 >......>TK。 你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入
输入的第一行是一个整数N(2<=N<=100),表示同学的总数。 第二行有n个整数,用空格分隔,第i个整数Ti(130≤Ti≤230)是第i位同学的身高(厘米)。
输出
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
8
186 186 150 200 160 130 197 220
样例输出
4
提示
数据规模 对于50%的数据,保证有n≤20; 对于全部的数据,保证有n≤100。
做法差不多,做两次最长上升子序列就可以了。(直接丢代码)
#include<cstdio>
#include<algorithm>
using namespace std;
int a[105],b[105],c[105];
int main()
{
int n,i,j,s=0;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&a[i]);
b[i]=1;c[i]=1;
}
for(i=2;i<=n;i++){
for(j=i-1;j>=1;j--){
if(a[j]<a[i])
b[i]=max(b[i],b[j]+1);
}
}
for(i=n-1;i>=1;i--){
for(j=i+1;j<=n;j++){
if(a[j]<a[i])
c[i]=max(c[i],c[j]+1);
}
}
for(i=1;i<=n;i++){
//printf("%d ",b[i]+c[i]);
if(b[i]+c[i]>s)
s=b[i]+c[i];
}
printf("%d",n-s+1);
}
四、排序+DP
渡轮问题
题目描述
输入
输出
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
30 4
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2
样例输出
4
4 2
10 3
15 12
17 17
一个排序加上一个最长上升子序列,做法很简单。只不过要注意一下输出。
#include<cstdio>
struct node{
int x,y;
}a[5005],t;
int b[5005][2];
int main()
{
int x,y,n,i,j,k,l;
scanf("%d%d%d",&x,&y,&n);
for(i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
b[i][0]=1;b[i][1]=0;
}
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
if(a[i].y>a[j].y){
t=a[i];
a[i]=a[j];
a[j]=t;
}
for(i=n-1;i>=1;i--){
l=0;k=0;
for(j=i+1;j<=n;j++){
if(a[j].x>a[i].x&&b[j][0]>l){
l=b[j][0];
k=j;
}
if(l>0){
b[i][0]=l+1;
b[i][1]=k;
}
}
}
k=1;
for(i=1;i<=n;i++)
if(b[i][0]>b[k][0]) k=i;
printf("%d\n",b[k][0]);
while(k!=0){
printf("%d %d\n",a[k].x,a[k].y);
k=b[k][1];
}
}