P1020 [NOIP1999 普及组] 导弹拦截
第一问的n^2 作法(最好想到的)
arr存数据,dp[i]存第i个元素是第k个被拦截的(也就是不上升序列的第几位),通过第二重循环,循环n次查找到dp[i]max。
for (int i = 2; i <= cnt; i++)
{
dp[i] = 1;//每个位置都默认为1
for (int j = 1; j < i; j++)
{
if (arr[i] <= arr[j])
{
dp[i] = max(dp[j] + 1, dp[i]); // 动态规划求最大值
}
}
}
第一问的nlogn作法(dp+二分+贪心)
怎么考虑logn?那么就需要想到查找时如何加快效率,只能用二分了,如果要用二分需要一个单调的数组,题目要求不上升序列,那么可以考虑用dp数组去存一个不上升序列的数组(怎么可以考虑?有一说一没想到)具体操作逻辑口述:外层循环从1~n,在循环到第i次的时候-----需要给arr[i]存放进dp数组中,构造成一个不上升序列,那么考虑dp[cnt](dp数组中最后一个元素,也就是从数组1~i-1最小的数) 如果dp[cnt]>=arr[i],那么dp[++cnt]=arr[i] 否则用二分找到arr[i]能排在dp数组中第几个数中。代码如下
for (int i = 2; i <= n; i++)
{
// cout << cnt << endl;
if (arr[i] <= dp[cnt])
dp[++cnt] = arr[i];
else
{ // 要知道这里dp是一个递减的序列了,我要找到第一小于arr[i]的下标并且替换(这里实际上想的是贪心,用更大的数替换一个等下标的数字,同时结果保存了,为了后续迭代更新)
int pos = upper_bound(dp + 1, dp + 1 + cnt, arr[i], greater<int>()) - dp; // 返回的是dp的下标(小于等于arr[i]的第一个元素哈,如果是相等也一样)
dp[pos] = arr[i];
}
}
看题解时upper_bound和lower_bound学习
upper_bound(num,num+n,k)是返还num数组中大于k的第一个元素的地址
lower_bound(num,num+n,k)是返还num数组中第一个大于或等于k的第一个元素的地址
因此要找到相应元素,应该int pos=upper_bound(num,num+n,k)-num;(如果没找到返回结尾元素下标)其中pos储存的是下标
lower_bound(num,num+n,k,greater<int>())是返还小于等于,upper_bound同理(这里注意数组要是从大到小,不存在的话会返回下标0)
第二问的nlogn做法(dp+二分+贪心)
如何考虑所需要的系统数目?同理用dp数组储存,dp[i]储存的是第i个系统最后一次抵挡的导弹的高度,由于logn,我们需要让dp继续保持一种单调性,而这种单调性是单调递增的,if(arr[i]>dp[cnt])则dp[++cnt]=arr[i],如果arr[i]小于dp[cnt],我们用二分找到dp数组中第一个大于arr[i]的下标(也就是所需要的最低能够抵挡该导弹的系统,此处思想就是贪心)dp数组的个数就是答案
代码如下:
for (int i = 2; i <= n; i++)
{
if (arr[i] > dp[cnt])
dp[++cnt] = arr[i];
else
{
int pos = lower_bound(dp + 1, dp + 1 + cnt, arr[i]) - dp;
dp[pos] = arr[i];
}
}
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n = 0;
const int N = 1e5 + 10;
int arr[N];
int dp[N];
int main()
{
while (cin >> arr[++n])
{
}
n--;
// 1~n存放题给的数据
dp[1] = arr[1];
int cnt = 1; // cnt是末尾指针
for (int i = 2; i <= n; i++)
{
if (arr[i] <= dp[cnt])
dp[++cnt] = arr[i];
else
{ // 要知道这里dp是一个递减的序列了,我要找到第一小于arr[i]的下标并且替换(这里实际上想的是贪心,用更大的数替换一个等下标的数字,同时结果保存了,为了后续迭代更新)
int pos = upper_bound(dp + 1, dp + 1 + cnt, arr[i], greater<int>()) - dp; // 返回的是dp的下标(小于等于arr[i]的第一个元素哈,如果是相等也一样)
dp[pos] = arr[i];
}
}
cout << cnt << endl;
cnt = 1;
memset(dp, 0, sizeof dp);
dp[1] = arr[1];
for (int i = 2; i <= n; i++)
{
if (arr[i] > dp[cnt])
dp[++cnt] = arr[i];
else
{
int pos = lower_bound(dp + 1, dp + 1 + cnt, arr[i]) - dp;
dp[pos] = arr[i];
}
}
cout << cnt << endl;
return 0;
}