ZOJ_3790_Consecutive Blocks(离散化+二分/单调队列)

题型:数据结构


题意:有n个方块,被涂上了一些颜色(color_type < 10^9),问至多拿掉k个方块之后,最大的相同颜色的连续方块数。


分析:

      由于n范围是10^5,我们就可以猜想,这个题目应该是nlogn的算法。

      可以这样思考,我们取一个起点和一个终点(两点方块的color相同),如果能够快速的算出这之间有多少个不同颜色的方块,就可以知道能不能删完这些不同颜色的方块以及能够得到的连续的相同颜色的方块(color与起点方块相同)。

      对于起点和终点,可以采用二分的思想,即枚举起点,二分终点。那么,就需要O(1)对起点与终点的方块进行处理了。

      对于这个问题,采用离散化的技巧。由于n范围是10^5,所以,color虽然范围很大,但是不同颜色数不会高出n,所以这样就可以离散化了,将颜色映射成1、2、3.......

      把离散化的color在原始数组里的下标存进vector里,例如:

                               1 1 1 2 2 3 2 2

      存为:         V[1]: 1 2 3

                         V[2]: 4 5 7 8

                         V[3]: 6

      这样,对于V[i],枚举起点二分终点,就可以秒算出可以得到的连续相同颜色的方块数了。

      一个优化:

      上述nlogn的算法已经可以AC了,大概O(9n)。使用单调队列处理的话,极限情况O(2n),这是一个常数上的小优化。

       单调队列,个人感觉有点队列的意思,也有点DP的意思,不过简单实用。

       对于单调函数,设置一前一后两个point,移动point并维护使其距离不超过k值,这样移动至最后,即为O(n)。

       对于此题,我们设置起点和终点两个指针,移动终点并计算两个指针之间需要删掉的最多的方块数;如果需要删掉的方块数大于k,那么就将起点后移一位。因为继续移动终点指针是没有意义的。


代码:

(1)离散化+二分

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<map>
#include<vector>
#define MAXN 123456
using namespace std;

int n,k;
int a[MAXN],b[MAXN];
vector<int> v[MAXN];


int main() {
	while(~scanf("%d%d",&n,&k)) {
		for(int i=0; i<n; i++) {
			scanf("%d",&a[i]);
			b[i] = a[i];
		}
		sort(b,b+n);
		b[n] = -1;
		//去重
		int len = 0;
		b[len] = b[0];
		for(int i=0; i<n; i++) {
			if(b[i] != b[i+1]) {
				b[len++] = b[i];
			}
		}

	//	for(int i=0; i<len; i++) {
	//		cout<<b[i]<<" ";
	//	}
	//	puts("");

		map<int,int> mp;
		mp.clear();

		int index = 0;
		for(int i=0; i<len; i++) {
			mp[b[i]] = ++index;
			v[i+1].clear();
		}
		for(int i=0;i<n;i++){
			v[mp[a[i]]].push_back(i);
		}
		int ans = 0;
		for(int i = 1; i <= index; i++) {
			int sz = v[i].size();
			for(int j = 0; j < sz; j++){
				int L = 0;
				int R = sz - 1;
				while(L <= R){
					int mid = (L + R) >> 1;
					if((v[i][mid]-v[i][j])-(mid-j) <= k){
						L = mid + 1;
						ans = max(ans,(mid-j+1));
					}
					else{
						R = mid - 1;
					}
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}
/*
8 1
1 1 1 2 2 3 2 2
 */


(2)离散化+单调队列

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<map>
#include<vector>
#define MAXN 123456
using namespace std;

int n,k;
int a[MAXN],b[MAXN];
vector<int> v[MAXN];


int main() {
	while(~scanf("%d%d",&n,&k)) {
		for(int i=0; i<n; i++) {
			scanf("%d",&a[i]);
			b[i] = a[i];
		}
		sort(b,b+n);
		b[n] = -1;
		//去重
		int len = 0;
		b[len] = b[0];
		for(int i=0; i<n; i++) {
			if(b[i] != b[i+1]) {
				b[len++] = b[i];
			}
		}

	//	for(int i=0; i<len; i++) {
	//		cout<<b[i]<<" ";
	//	}
	//	puts("");

		map<int,int> mp;
		mp.clear();

		int index = 0;
		for(int i=0; i<len; i++) {
			mp[b[i]] = ++index;
			v[i+1].clear();
		}
		for(int i=0;i<n;i++){
			v[mp[a[i]]].push_back(i);
		}

		int ans = 0;
		for(int i=1;i<=index;i++){
			int s = 0;
			int t = 0;
			int sz = v[i].size();
			while(t<sz){
				if((v[i][t]-v[i][s])-(t-s)<=k){
					ans = max(ans,(t-s+1));
					t++;
				}
				else{
					s++;
				}
			}

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

	}
	return 0;
}
/*
8 1
1 1 1 2 2 3 2 2
 */


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值