(1)acwing 4557. 最长上升子序列 4557. 最长上升子序列 - AcWing题库
给定一个长度为 N 的整数序列 a1,a2,…,aN。请你计算该序列的最长上升子序列的长度。上升子序列是指数值严格单调递增的子序列
7
1 7 3 5 9 4 8
4
C++代码:
#include
#include
using namespace std;
const int N = 1010;
int n,res=0;
int arr[N],dp[N];
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&arr[i]);
for(int i=1;i<=n;i++) {
dp[i]=1;
for(int j=1;j
也可以这样写:
int main() {
scanf("%d",&n);
for(int i=0;i
(2)acwing1017-怪盗基德的滑翔翼
假设城市中一共有 N幢 建筑排成一条线,每幢建筑的高度各不相同。初始时,怪盗基德可以在任何一幢建筑的顶端。他可以选择一个方向逃跑,但是不能中途改变方向(因为中森警部会在后面追击)。因为滑翔翼动力装置受损,他只能往下滑行(即:只能从较高的建筑滑翔到较低的建筑)。他希望尽可能多地经过不同建筑的顶部,这样可以减缓下降时的冲击力,减少受伤的可能性。请问,他最多可以经过多少幢不同建筑的顶部(包含初始时的建筑)?
输入格式
输出格式
数据范围
输入样例:
3
8
300 207 155 299 298 170 158 65
8
65 158 170 298 299 155 207 300
10
2 1 3 4 5 6 7 8 9 10
输出样例:
6
6
9
思路:移动方向一旦确定,就转换成了LIS问题,那么可以看作是两个方向的最长上升子序列问题,答案res为正向和逆向LIS两个过程中的较大者。
// 当确定完方向和起点后,最长的距离是什么呢?
// 起点:a[i]
// 最长距离:以a[i]为结尾的最长上升子序列
#include
#include
using namespace std;
const int N = 110;
int n;
int arr[N],dp[N];
int main() {
int T;
scanf_s("%d", &T);
while (T--) {
scanf_s("%d", &n);
for (int i = 1; i <= n; i++) scanf_s("%d", &arr[i]);
// 正向求解LIS问题
int res = 0;
for (int i = 1; i <= n; i++) {
dp[i] = 1;
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) dp[i] = max(dp[i], dp[j] + 1);
}
res = max(res, dp[i]);
}
// 反向求解LIS问题
for (int i = n; i>=1; i--) {
dp[i] = 1;
for (int j = n; j > i; j--) {
if (arr[i] > arr[j]) dp[i] = max(dp[i], dp[j] + 1);
}
res = max(res, dp[i]);
}
printf("%d\n", res);
}
return 0;
}
(3)acwing1014-登山问题
五一到了,ACM队 组织大家去登山观光,队员们发现山上一共有N个景点,并且决定按照顺序来浏览这些景点,即每次所浏览景点的编号都要大于前一个浏览景点的编号。同时队员们还有另一个登山习惯,就是不连续浏览海拔相同的两个景点,并且一旦开始下山,就不再向上走了。队员们希望在满足上面条件的同时,尽可能多的浏览景点,你能帮他们找出最多可能浏览的景点数么?
输入格式
输出格式
数据范围
输入样例
8
186 186 150 200 160 130 197 220
输出样例
4
本题实质上是求正向和反向两次LIS问题,两次的LIS过程相互独立,故所求为两端LIS过程中最长上升子序列的最大长度之和
C++代码:
/*
条件1:按照编号递增的顺序来浏览 => 必须是子序列
条件2:相邻两个景点不能相同
条件3:一旦开始下降,就不能上升了
目标:求最多能浏览多少景点
*/
#include
#include
using namespace std;
const int N = 1010;
int n;
int arr[N];
int f[N], g[N];
int main() {
scanf_s("%d", &n);
for (int i = 1; i <= n; i++) scanf_s("%d", &arr[i]);
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++) {
if (arr[i] > arr[j]) f[i] = max(f[i], f[j] + 1);
}
}
for (int i = n; i >= 1; i--) {
g[i] = 1;
for (int j = n; j > i; j--) {
if (arr[i] > arr[j]) g[i] = max(g[i], g[j] + 1);
}
}
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, f[i] + g[i] - 1);
printf("%d\n", res);
return 0;
}
(4)acwing 482.合唱队形 482. 合唱队形 - AcWing题库
N 位同学站成一排,音乐老师要请其中的 (N−K) 位同学出列,使得剩下的 K 位同学排成合唱队形。合唱队形是指这样的一种队形:设 K 位同学从左到右依次编号为 1,2…,K 他们的身高分别为 T1,T2,…,TK 则他们的身高满足 T1<…
思路: 如果要使得出列的同学数量最少的话,就意味着要使剩下的数量最多。那意思就是要找到一个先上升再下降的序列,选个最大的。然后再用总数减去这个长度。
#include
#include
using namespace std;
const int N = 1010;
int n;
int arr[N];
int f[N],g[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &arr[i]);
for(int i=1;i<=n;i++) {
f[i] = 1;
for(int j=1;jarr[j]) f[i] = max(f[i], f[j] + 1);
}
}
for(int i=n;i>=1;i--) {
g[i] = 1;
for(int j=n;j>i;j--) {
if(arr[i]>arr[j]) g[i] = max(g[i], g[j] + 1);
}
}
int res=0;
for(int i=1;i<=n;i++) res = max(res,f[i]+g[i]-1);
printf("%d",n-res);
return 0;
}
(5)acwing 1012.友好城市
输入样例:
7
22 4
2 6
10 3
15 12
9 8
17 17
4 2
输出样例:
4
>>思路和分析
对于任意一种合法的建立桥梁的方式,都可以对应一种因变量的上升子序列。反之,对于因变量的任何一个上升子序列,我们都可以唯一的对应一个合法的建桥方式。每一种建桥方式,建桥的数量和上升子序列的长度是相同的。因此左边集合(所有合法的建桥方式)的长度最大值就等于右边集合(上升子序列)的长度的最大值。
解法:先按照自变量的大小,把因变量排序。排好之后得到一个序列,然后在序列当中求最长的一个上升子序列。
排序思路:看一下什么会出现相交的情况?
本质:如果选出来的桥梁是没有交叉的,那么就意味着按照其中某一个点来排序,那么另外一个点对应的位置也一定要使有序的才可以。因为一旦出现逆序,就是有交叉的。没有交叉也就意味着一定没有逆序的。那本题其实就可以转化为求最长上升子序列的长度。
C++代码:
#include
#include
using namespace std;
typedef pair PII;
const int N = 5010;
int n;
PII q[N];
int f[N];
int main() {
scanf_s("%d", &n);
for (int i = 0; i < n; i++) scanf_s("%d%d", &q[i].first, &q[i].second);
sort(q, q + n);
int res = 0;
for (int i = 0; i < n; i++) {
f[i] = 1;
for (int j = 0; j < i; j++) {
if (q[i].second > q[j].second) f[i] = max(f[i], f[j] + 1);
}
res = max(res, f[i]);
}
printf("%d\n",res);
return 0;
}
(6)acwing 1016.最长上升子序列和
输入样例:
7
1 7 3 5 9 4 8
输出样例:
18
#include
#include
using namespace std;
const int N = 1010;
int n;
int a[N], f[N];
int main() {
scanf_s("%d", &n);
for (int i = 1; i <= n; i++) scanf_s("%d", &a[i]);
for (int i = 1; i <= n; i++) {
f[i] = a[i];
for (int j = 1; j a[j]) f[i] = max(f[i], f[j] + a[i]);
}
}
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, f[i]);
printf("%d\n", res);
return 0;
}