DP_二分搜索 or LIS

题意:

1294 修改数组

给出一个整数数组A,你可以将任何一个数修改为任意一个正整数,最终使得整个数组是严格递增的且均为正整数。问最少需要修改几个数?

输入
第1行:一个数N表示序列的长度(1 <= N <= 100000)。
第2 - N + 1行:每行1个数,对应数组元素。(0 <= A[i] <= 10^9)

输出
输出最少需要修改几个数使得整个数组是严格递增的。

输入样例
5
1
2
2
3
4

输出样例
3

思路:

(1) 题目要求是严格递增的qaq。。首先考虑第一种i > a[i] 。。这种肯定是要改的,为什么呢?我们题目要求的是正整数所以a[i]最小就是i了 --> 对于a[i] < i这种情况必须要改

if(a[i] - i >= 0) 
	b[++cnt] = a[i] - i;
else cur++;//一定需要改动的部分。。

(2)对于剩下的可能要改,可能不改的数,先把他们存到一个新的数组里面。到底怎么判断改还是不改呢???
它们在原数组里一定要满足对于i < j , a[j] - a[i] >= j - i,就有a[j] - j >= a[i] - i。。所以对于每一个不满足条件1的a[i],把a[i] - i存到一个新的数组里,找b[i]的不严格递增的最长子序列的长度,这个就是不需要改动的数目。。。

(3)n - 不需要改动的数目 = ans

注意:我们知道求最长上升子序列可以dp,但是数据范围比较大。。会T掉。。。求不严格递增的子序列的数目我们也可以二分求解
二分代码如下:

			int l = 1;
			int r = now;
			int ans;
			while(l <= r){
				int mid = (l + r) / 2;
				if(a[mid] > b[i]){
					r = mid - 1;
					ans = mid;
				}
				else{
					l = mid + 1;
				}
			}
			a[ans] = b[i];

代码实现:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<set>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
const int inf = 1e9 + 5;

int b[maxn];
int cnt = 0;
int lis(){
}
int main(){
    int n;
    int a[maxn];
    scanf("%d",&n);
    int cur = 0;
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
        if(a[i] - i >= 0) b[++cnt] = a[i] - i;
        else cur++;//一定需要改动的部分。。
    }
    memset(a,0,sizeof(a));
    int now = 0;
    a[now] = -1;
    for(int i = 1;i <= cnt;i++){
    	if(b[i] >= a[now]){
    		a[++now]  = b[i];
		}
		else{
			int l = 1;
			int r = now;
			int ans;
			while(l <= r){
				int mid = (l + r) / 2;
				if(a[mid] > b[i]){
					r = mid - 1;
					ans = mid;
				}
				else{
					l = mid + 1;
				}
			}
			a[ans] = b[i];
		}
	}
	printf("%d\n",cur + cnt - now);
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值