noiac!B. delete

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq872425710/article/details/82816677

题目大意:

长度为n的序列A,从中删去恰好k个元素(右边的元素往左边移动),记cnt为新序列中Ai=i的元素个数(即权值与下标相同的元素的个数)。求cnt的最大值。

思路:

一开始肯定是想到dp,f[i][j]为前i个删了j个的最大值,显然状态不怎么好转移,那就设改改想法,设f[i][j]为前i个取了j个。然后得出dp方程j==a[i]的时候加一,不然就是背包问题选或者不选。这时候!!我发现,你把i,j做一个坐标系,就可以把问题转化成一个坐标系上,一些点有点值,然后每次可以向下向右下走。,求走到n,(n-k)的最大权值,然后过程可以线段树优化…我没打线段树优化…

程序:

朴素dp

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 5005
int n,k,ans;
int a[N],f[N];
using namespace std;
int main(){
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++){
		for (int j=n-k;j>=1;j--)
		 if (j==a[i]) f[j]=max(f[j],f[j-1]+1);
	 			else f[j]=max(f[j],f[j-1]);
	}
	printf("%d",f[n-k]);
}

线段树优化

`#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 100005
using namespace std;
int n,k,ans,o;
int a[N],f[N];
struct tree{int max;}t[N*8];
void insert(int rt,int l,int r,int x,int y){
	if (l==r){
		t[rt].max=max(t[rt].max,y);
		return;
	}
	int mid=(l+r)/2;
	if (x<=mid) insert(rt*2,l,mid,x,y);
			else insert(rt*2+1,mid+1,r,x,y);
	t[rt].max=max(t[rt*2].max,t[rt*2+1].max);
}

void find(int rt,int l,int r,int x,int y){
	if (l==x&&r==y){
		o=max(o,t[rt].max);
		return;
	}
	int mid=(l+r)/2;
	if (mid<x) find(rt*2+1,mid+1,r,x,y);
	else if (mid>=y) find(rt*2,l,mid,x,y);
	else {
		find(rt*2,l,mid,x,mid);
		find(rt*2+1,mid+1,r,mid+1,y);
	}
}

int main(){
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++){
		o=0;
		if (i-a[i]<=0) o=a[0];
		int x=max(1,i-a[i]);
		find(1,1,n,x,i);
		f[i]=o+1;
		if (x==0) a[0]=max(a[0],f[i]);
				else insert(1,1,n,x,f[i]);
		if (n-i>=(n-k)-a[i]) ans=max(ans,f[i]);
	}
	printf("%d",ans);
}

猜你喜欢

转载自blog.csdn.net/qq872425710/article/details/82816677