参考:https://oi-wiki.org/dp/basic/
O(n^2)算法
每一次从头扫描找出最佳答案。
int a[MAXN], d[MAXN];
int dp() {
d[1] = 1;
int ans = 1;
for (int i = 2; i <= n; i++) {
for (int j = 1; j < i; j++)
if (a[j] <= a[i]) {
d[i] = max(d[i], d[j] + 1);
ans = max(ans, d[i]);
}
}
return ans;
}
O(nlogn)算法二分法
参考了这篇文章 https://www.cnblogs.com/itlqs/p/5743114.html
那么代码如下:
for (int i = 0; i < n; ++i) scanf("%d", a + i);
memset(dp, 0x1f, sizeof dp);
mx = dp[0];
for (int i = 0; i < n; ++i) {
*std::upper_bound(dp, dp + n, a[i]) = a[i];
}
ans = 0;
while (dp[ans] != mx) ++ans;
描述
某国为了防御敌国的导弹袭击,研发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试验阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
格式
输入格式
输入数据只有一行,该行包含若干个数据,之间用半角逗号隔开,表示导弹依次飞来的高度(导弹最多有 20 枚,其高度为不大于 30000 的正整数)。
输出格式
输出数据只有一行,该行包含两个数据,之间用半角逗号隔开。第一个数据表示这套系统最多能拦截的导弹数;第二个数据表示若要拦截所有导弹至少要再添加多少套这样的系统。
样例1
样例输入1
389,207,155,300,299,170,158,65
样例输出1
6,1
限制
每个测试点限时 1 秒。
解题
代码
#include <algorithm>
#include <iostream>
#include <stack>
#include <string>
#include <vector>
using namespace std;
const int maxn = 1e5 + 2;
int dp[maxn]; // dp[k]表示长度为k的不下降子序列末尾元素的最小值
// int minh[maxn]; //记录从第i项开始,剩下最大能消灭的导弹数量
vector<int> v;
int up_bound(int l, int r, int x) { //找到第一个大于x的数的下标
while (l < r) {
int mid = (r + l) / 2;
if (x >= dp[mid])
l = mid + 1;
else
r = mid;
}
return l;
}
int down_bound(int l, int r, int x) { //找到第一个小于等于x的数的下标
for (int i = l; i <= r; i++)
if (dp[i] <= x) return i;
return r;
}
int main() {
char ch;
int n;
while (true) {
scanf("%d", &n);
v.push_back(n);
ch = getchar();
if (ch == '\n') break;
}
v.push_back(0);
reverse(v.begin(), v.end());
int len = 1;
dp[1] = v[1];
for (int i = 2; i < v.size(); i++) { //求出最长不降子序列
if (v[i] >= dp[len]) {
dp[++len] = v[i];
} else {
int up = up_bound(1, len, v[i]);
dp[up] = v[i];
}
}
cout << len << ",";
//第二问就出最大下降子序列
len = 1;
dp[1] = v[1];
for (int i = 2; i < v.size(); i++) {
if (v[i] < dp[len])
dp[++len] = v[i];
else {
int down = down_bound(1, len, v[i]);
dp[down] = v[i];
}
}
cout << len - 1 << endl;
return 0;
}
/*90 103 99 83 102 70 86 70 99 71*/