USETC 1501 Defence Linces(DP+线段树+离散化)

题目链接

题意: 允许扣去一个连续子串的情况下,求剩下串的最长连续增长子串。  长度<= 200000.  数据大小: 1e9。


第一次使用离散化。其核心是: 利用数据个数相对较少,将N个数据建立与整数1~N的映射。 方法是, 先排序 ,再二分查早序号。

而线段树可以用来维护区间最大值, 这里是最大“没有间隔的连续子串长度”。

离散化和线段树只是用来优化的,下面解释一下算法主题:DP

个人认为动态规划最重要的是状态表示和状态转移。  

①    dp[ max_len ][ 2 ]   ;            dp[ i ][ 0 ] 表示以第 i 个数结尾的不含间隔的最长增长子串长度,dp[ i ][ 1 ]表示以第 i 个数结尾的含间隔的最长增长子串长度。

②    dp [ i ][ 0 ]相对容易计算,    dp[ i ][ 0 ] = a[ i ] > a [ i-1 ] ? dp[ i-1 ][ 0 ] + 1  :  1.

而dp[ i ][ 1 ]来自两方面: 一是继承了dp[ i-1 ][ 1 ]的间隔(此时a[ i ] > a[ i-1 ]),二是 a[ i ]的前面就是间隔 ,此时需要知道以 a[ x ] (a[ x ]<a[ i ] && x < i)结尾的连续子串的最大长度。

      有个问题困惑了很久,如果找不到间隔怎么办?  dp[ i ][ 1 ]是否失去了意义? 阳神一句精辟的话提醒了我: 不影响结果。  


代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define Lson (cur)<<1,L,mid
#define Rson  (cur)<<1|1,mid+1,R

const int maxn=200050;
int dp[maxn][2],a[maxn],LiSan[maxn];
int N;
int Tree[maxn<<2];


int Find(int x){
	int Low=1,High=N,mid;
	while(Low<High){
		mid=(Low+High)>>1;
		if(LiSan[mid]>=x) High=mid;
		else Low=mid+1;
	}
	return Low;
}
void update(int cur){
	Tree[cur] = max (Tree[cur] , Tree[cur<<1]);
	Tree[cur] = max (Tree[cur] , Tree[(cur<<1)|1]);
}
void Insert(int cur,int L,int R,int l,int r,int x){
	if(l<=L && R<=r){
	       Tree[cur]=max(Tree[cur] , x); return ;
	}
	int mid=(L+R)>>1;
	if(l<=mid) Insert(Lson,l,r,x);
	if(r>mid)  Insert(Rson,l,r,x);
	update(cur);
}

int Query(int cur,int L,int R,int l,int r){
	if(l<=L && R<=r){
		return Tree[cur];
	}
	int mid=(L+R)>>1;
	int ans=0;
	if(l<=mid) ans=max(ans,Query(Lson,l,r));
	if(r>mid)  ans=max(ans,Query(Rson,l,r));
	return ans;
}

int main()
{
	//freopen("D:/input.txt","r",stdin);
	//freopen("D:/output.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--){
		memset(Tree,0,sizeof(Tree));
		memset(dp,0,sizeof(dp));

		scanf("%d",&N);
		for(int i=1;i<=N;i++){
			scanf("%d",&a[i]);  LiSan[i]=a[i];
		}
		sort(LiSan+1,LiSan+N+1);
        int ans=0;
		dp[1][0]=1;  dp[1][1]=1;
		int p=Find(a[1]);
		Insert(1,1,N,p,p,1);
		ans=max(ans,dp[1][0]), ans=max(ans,dp[1][1]);

		for(int d=2;d<=N;d++){
			dp[d][0] = a[d]>a[d-1] ? dp[d-1][0]+1 : 1;
			p=Find(a[d]);
			Insert(1,1,N,p,p,dp[d][0]);

			if(a[d] > a[d-1]) dp[d][1]=dp[d-1][1]+1;
			int tmp=0;
			if(p>1) tmp=Query(1,1,N,1,p-1);
			dp[d][1] = max(dp[d][1] , tmp+1);

			ans=max(ans,dp[d][0]),ans=max(ans,dp[d][1]);
		}

		printf("%d\n",ans);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值