空档接龙游戏

此题目来自bzoj.org/p/Z2149

题目

类似电脑中的"空档接龙“游戏

给定N个数字,其为1到N的某个全排列。

对于每一个数字,找到已经生成的栈中栈顶大于等于该数字的最小值,并将该数字存入到栈顶

每当一个栈内的数据总量达到K时,则“吃掉”这个栈,被“吃掉”的栈不能再存入新元素。

依次输出从1到N每一个元素被“吃掉”的时间。(对于没被吃的元素输出-1)

N<=1e6 

小插曲:1e6==1000000==10^6 

如果有人做过“奇怪的排序”会发现十分十分的相似。

这道题同样用到单调栈。

这题要用到很多的栈,有一些人就开很多个栈,然后被炸的找不到北(你问我为什么知道?因为我就被炸过……)

其实,我们可以用几个数组来实现n个栈:

sizen[x]=y,代表第x个栈的大小
ans[x]=y,代表数字x在第y个时间被消去
under[x]=y,代表在栈中,数字x的下面是哪个数字
top[x]=y,代表在第x个栈中,其栈顶的元素的y
idx:代表一共有多少上栈

核心思路:

无论如何开栈,最后的读数永远是从左往右,从顶到底的弹出。

所以,我们的单调栈必须是严格上升的,为了保证最优解,每一个数要放在栈顶元素大于这个数且栈顶元素尽量小的栈里

于是每读入一个元素x,看其是否大于top[idx]
如果是的,则新开一个栈出来,并记录相关信息。

否则在top数组中进行二分查找,得到数字T
代表x应该被加入哪个栈中,于是这个栈要变大
并更换栈顶元素为x,并记录under[x]=top[T]

最后看下size[t]是否等于k,如果满足话
则将栈中所有元素倒出来,并记录其在第i个时间被 消掉

最最后在将top数组中将所有[t+1,idx]间的元素
左移一位,注意栈的栈顶元素,栈的大小要更新信息

代码(我的):

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+10;
int cd[N],ans[N],under[N],top[N],idx,sum;

int main(){
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		sum++;
		int x;
		cin>>x;
		if(top[idx]<x){
			idx++;
			top[idx]=x;
			cd[idx]=1;
			if(cd[idx]>=k){
				int w=top[idx];
				ans[w]=sum;
				idx--;
			}
		}
		else{
			int l=1,j=idx,T=0;
			while(l<=j){
				int mid=(l+j)/2;
				if(top[mid]>x)
					j=mid-1,T=mid;
				else
					l=mid+1;
			}
			under[x]=top[T];
			top[T]=x;
			cd[T]++;
			if(cd[T]>=k){
				int w=top[T];
				while(w!=0){
					ans[w]=sum;
					w=under[w];
				}
				for(int j=T;j<=idx;j++){
					cd[j]=cd[j+1];
					top[j]=top[j+1];
				}
				idx--;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(ans[i]==0)
			cout<<-1<<endl;
		else
			cout<<ans[i]<<endl;
	}
	return 0;
}

经过优化的:

#include <bits/stdc++.h>
#define N 200007
using namespace std;
int n, k, x, sizen[N], ans[N], under[N], top[N], idx;
int main() {
    memset(ans, -1, sizeof ans);
    scanf("%d%d", &n, &k);
    for ( int i = 1, t; i <= n; ++i)
    {
        scanf("%d", &x);
        if(x > top[idx])
        //开出一个top数组,存在每个栈的栈顶元素
        {
            top[++idx] = x;
            sizen[idx] = 1;
            t=idx;
        }
        else
        {
               t = upper_bound(top+1,top+1+idx,x)-top;//找出x应该放在哪个栈中
               sizen[t]++;
               under[x] = top[t];
               top[t] = x;
        }
        if(sizen[t] == k) //如果x所在的栈的元素有了k个
        {
            while(x)
            {
                ans[x] = i; //第x个元素在第i个时间被消掉
                x = under[x]; //取出x下方的元素
             
            }
            for ( int j = t; j < idx; ++j)
                top[j] = top[j + 1],sizen[j]=sizen[j+1];
            --idx;
        }
    }
    for ( int i = 1; i <= n; ++i)
        printf("%d\n", ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值