动态dp法
[原题]https://www.acwing.com/problem/content/description/897/
(动态规划) O(n2)O(n2)
状态表示:dp[i]表示从第一个数字开始算,以num[i]结尾的最大的上升序列。(以num[i]结尾的所有上升序列中属性为最大值的那一个)
状态计算(集合划分):j∈(0,1,2,..,i-1), 在num[i] > num[j]时,
dp[i] = max(dp[i], dp[j] + 1) // dp[j]+1->前一个小于自己的数结尾的最大上升子序列加上自己,即+1
有一个边界,若前面没有比i小的,dp[i]为1(自己为结尾)。
最后在找dp[i]的最大值。
时间复杂度
O(n2)O(n2) 状态数(nn) * 转移数(nn)
//动态dp板子 .cpp
#include <iostream>
#include<vector>
#include<cstdio>
#include <algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N = 1010;
int num[N], dp[N];
int main()
{
int n;
cin >> n;
int i, j;
for (i = 0; i < n; i++)
{
cin >> num[i];
}
int mx = 1;
for (i = 0; i < n; i++)
{
dp[i] = 1;//默认为1,找不到前面的数字小于自己的时候就为1;
for (j = 0; j < i; j++)
{
if (num[i] > num[j])
dp[i] = max(dp[i], dp[j] + 1); //动态dp板子
}
mx = max(mx, dp[i]);
}
cout << mx << endl;
return 0;
}基于优化目的
动态dp+二分法
1.状态表示:dp[i]表示长度为i的最长上升子序列,末尾最小的数字。(长度为i的最长上升子序列所有结尾中,结尾最小min的) 即长度为i的子序列末尾最小元素是什么。
2.状态计算:对于每一个num[i], 如果大于dpcnt-1,那就cnt+1,使得最长上升序列长度+1,当前末尾最小元素为num[i]。 若num[i]小于等于dp[cnt-1],说明不会更新当前的长度,但之前末尾的最小元素要发生变化,找到第一个 大于或等于 (这里不能是大于) num[i],更新以那时候末尾的最小元素。
3.dp[i]一定以一个单调递增的数组,所以可以用二分法来找第一个大于或等于num[i]的数字。
时间复杂度
O(nlogn)O(nlogn) 状态数(nn) * 转移数(lognlogn)
//动态dp板子 .cpp
#include <iostream>
#include<vector>
#include<cstdio>
#include <algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N = 1010;
int num[N], dp[N];
int main()
{
int n,cnt=0;
cin >> n;
int i, j;
for (i = 0; i < n; i++)
{
cin >> num[i];
}
dp[cnt++] = num[0];
for (i = 1; i < n; i++)
{
if (num[i] > dp[cnt - 1])
dp[cnt++] = num[i];
else
{
int left = 0,right = cnt - 1;
while (left < right)
{
int mid = (left + right) >> 1;
if (dp[mid] >= num[i])
right = mid;
else
left = mid + 1;
}
dp[right] = num[i];
}
}
cout << cnt << endl;
return 0;
}[482. 合唱队形]https://www.acwing.com/problem/content/484/
#include <iostream>
#include<vector>
#include<cstdio>
#include <algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
const int N = 110;
int num[N] = { 0 }, dp1[N] = {0}, dp2[N] = {0};
int main()
{
int n;
cin >> n;
int i, j, k;
for (i = 0; i < n; i++)
{
cin >> num[i];
}
for (i = 0; i < n; i++)
{
dp1[i] = 1;//默认为1,找不到前面的数字小于自己的时候就为1;
for (j = 0; j < i; j++)
{
if (num[i] > num[j])
dp1[i] = max(dp1[i],dp1[j] + 1); //动态dp板子
}
}
for (i = n - 1; i >= 0; i--)
{
dp2[i] = 1;
for (j = n - 1; j > i; j--)
{
if (num[i] > num[j])
dp2[i] = max(dp2[i], dp2[j] + 1);
}
}
int mx = 1;
for (k = 0; k < n; k++)
mx = max(mx, dp1[k] + dp2[k] - 1);
cout << n - mx << endl;
return 0;
}


京公网安备 11010502036488号