2022.12.26动态规划练习
洛谷P1091 [NOIP2004 提高组] 合唱队形
题目描述
\(n\) 位同学站成一排,音乐老师要请其中的 \(n-k\) 位同学出列,使得剩下的 \(k\) 位同学排成合唱队形。
合唱队形是指这样的一种队形:设 \(k\) 位同学从左到右依次编号为 \(1,2,\) … \(,k\),他们的身高分别为 \(t_1,t_2,\) … \(,t_k\),则他们的身高满足 \(t_1< \cdots <t_i>t_{i+1}>\) … \(>t_k(1\le i\le k)\)。
你的任务是,已知所有 \(n\) 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式
共二行。
第一行是一个整数 \(n\)(\(2\le n\le100\)),表示同学的总数。
第二行有 \(n\) 个整数,用空格分隔,第 \(i\) 个整数 \(t_i\)(\(130\le t_i\le230\))是第 \(i\) 位同学的身高(厘米)。
输出格式
一个整数,最少需要几位同学出列。
样例 #1
样例输入 #1
8
186 186 150 200 160 130 197 220
样例输出 #1
4
提示
对于 \(50\%\) 的数据,保证有 \(n \le 20\)。
对于全部的数据,保证有 \(n \le 100\)。
思路
思路很简单。就是选择出最多的人构成合唱团,且是山峰型的。
状态表示:\(l[i]\) 和 \(r[i]\) 分别表示从 \(1-i\) 的上升子序列和 \(i-n\) 的下降子序列。那么我们需要求这两个的最大值,那么最大的 \(l[i]+r[i]-1\) 即是最多的人,\(n-(l[i]+r[i]-1)\) 即是最少去除的人。
状态计算:状态计算思路同最长上升子序列,考虑 \(l[i]\),即以 \(t[i]\) 为结尾的上升子序列,那么我们考虑它的前一个元素 \(t[j],1≤j<i\),则如果 \(t[i]>t[j]\),\(l[i]=max(l[i], l[j]+1)\)。\(r[i]\) 的计算类似。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110, MOD = 1e6 + 7;
typedef pair<int, int> PII;
typedef long long LL;
int n;
int t[N];
int l[N], r[N]; // l[i]表示从1-i中升序,r[i]表示i-n的降序
int main ()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
{
scanf("%d", &t[i]);
l[i] = r[i] = 1;
}
for (int i = 1; i <= n; i ++)
for (int j = 1; j < i; j ++)
if (t[i] > t[j])
l[i] = max(l[i], l[j] + 1);
for (int i = n; i >= 1; i --)
for (int j = n; j > i; j --)
if (t[j] < t[i])
r[i] = max(r[i], r[j] + 1);
int res = 0;
for (int i = 1; i <= n; i ++)
res = max(l[i] + r[i] - 1, res);
cout << n - res << endl;
return 0;
}
P1233 木棍加工
题目描述
一堆木头棍子共有 \(n\) 根,每根棍子的长度和宽度都是已知的。棍子可以被一台机器一个接一个地加工。机器处理一根棍子之前需要准备时间。准备时间是这样定义的:
第一根棍子的准备时间为 \(1\) 分钟;
如果刚处理完长度为 \(L\),宽度为 \(W\) 的棍子,那么如果下一个棍子长度为 \(L_i\),宽度为 \(W_i\),并且满足 \(L≥L_i,W≥W_i\),这个棍子就不需要准备时间,否则需要 \(1\) 分钟的准备时间;
计算处理完 \(n\) 根棍子所需要的最短准备时间。比如,你有 \(5\) 根棍子,长度和宽度分别为 \((4, 9)\),\((5, 2)\),\((2, 1)\),\((3, 5)\),\((1, 4)\),最短准备时间为 \(2\)(按 \((4, 9)\)、\((3, 5)\)、\((1, 4)\)、\((5, 2)\)、\((2, 1)\) 的次序进行加工)。
输入格式
第一行是一个整数 \(n(n≤5000)\),第 \(2\) 行是 \(2n\) 个整数,分别是 \(L_1,W_1,L_2,w_2,…,L_n,W_n\)。\(L\) 和 \(W\) 的值均不超过 \(10000\),相邻两数之间用空格分开。
输出格式
仅一行,一个整数,所需要的最短准备时间。
样例 #1
样例输入 #1
5
4 9 5 2 2 1 3 5 1 4
样例输出 #1
2
思路
首先按照长度和宽度从小到大排序,这是贪心,那么就一定有 \(L[i]≤L[i+1]\),这里我们是先比较长度。那么我们就只需要考虑宽度即可。根据 \(dilworth\) 定理,链的最少划分数=反链的最长长度,那么一个序列最少可以分成 \(n\) 个最长不下降子序列,这个序列最长下降子序列长度为 \(m\),则 \(n=m\)。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5010, MOD = 1e6 + 7;
typedef pair<int, int> PII;
typedef long long LL;
int n;
struct Q
{
int l, w;
} q[N];
bool cmp(Q x, Q y)
{
if (x.l != y.l) return x.l < y.l; // 按长度排序
return x.w < y.w;
}
int f[N];
int main ()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
scanf("%d%d", &q[i].l, &q[i].w);
sort(q + 1, q + n + 1, cmp);
for (int i = 2; i <= n; i ++)
for (int j = 1; j < i; j ++)
if (q[j].w > q[i].w)
f[i] = max(f[j] + 1, f[i]);
int ans = 0;
for (int i = 1; i <= n; i ++)
ans = max(ans, f[i]);
cout << ans + 1 << endl;
return 0;
}
参考
本文来自博客园,作者:Cocoicobird,转载请注明原文链接:https://www.cnblogs.com/Cocoicobird/p/17009054.html
浙公网安备 33010602011771号