码迷,mamicode.com
首页 > 其他好文 > 详细

【数据结构】k维滑窗:扩展到k维的单调队列

时间:2020-04-29 01:12:45      阅读:61      评论:0      收藏:0      [点我收藏+]

标签:友谊   sdi   遍历   block   分割   queue   turn   char   line   

k维滑窗:扩展到k维的单调队列

假如要求一个高维度的空间(数组,矩阵,长方体,这些固定窗口型区域)的固定区域极值时,且满足高纬度空间是在复杂度范围内时,可以用这个做法, 时间复杂度为 \(\mathcal{O(高维空间容量\times 维度)}\)。这个维度只是一个小常数,可以忽略,所以也可以说,时间复杂度为 \(\mathcal{O(高维空间容量)}\)

做法, 以求极大值为例:

首先,是一维, 假设长度为 \(n\) 的数组,对于所有\(i\) ,要求所有 区间\([i,min(i+d,n)]\) 里的区间最大值,那就是一维滑窗:

\(code:\)

#pragma GCC optimize("-O2")
#include<bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=2e3+5;
const double ep=1e-8;

struct Q{
	int v,id;
}q[maxn];//队列 
int a[maxn];//原数组 
int b[maxn];//结果 
int main()
{
	int n,d;
	scanf("%d%d",&n,&d);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	int l=1,r=0;
	for(int i=1;i<=d+1;i++)
	{
		
		while(r>=l&&q[r].v<=a[i])r--;
		q[++r]={a[i],i};
	}
	b[1]=q[l].v;
	for(int i=2;i<=n;i++)
	{
		while(q[l].id<i&&l<=r)l++;
		if(i+d<=n)
		{
			while(r>=l&&q[r].v<=a[i+d])r--;
			q[++r]={a[i+d],i+d};
		}
		b[i]=q[l].v;
	}
	for(int i=1;i<=n;i++)printf("%3d",a[i]);putchar(10);
	for(int i=1;i<=n;i++)printf("%3d",b[i]);
}

其次是二维, \(n\times m\) 矩阵,要求 \(\ d_n\times d_m\) 内窗口的极大值:

  • 首先把二维数组分成 \(n\) 个长度为 \(m\) 的一维数组, 先处理好每个一维数组,处理好每个以 \(i\) 为起点 \(i+d_m\) 为终点的区间极值, 更新到新数组中。
  • 其次,再以新的数组, 将其分成 \(m\) 个长度为 \(n\) 的一维数组, 再次处理每个一维数组,处理好每个以 \(i\) 为起点 \(i+d_n\) 为终点的区间极值, 更新到另一个新数组中, 得到的新数组就是答案。

三维同理,将三维分割成第三维度个 \(\ n\times m\) 的二维数组,再处理。

例题:

牛客 “科大讯飞杯”第18届上海大学程序设计联赛春季赛暨高校网络友谊赛: K 迷宫

题意:

有一个 \(n\times m\) 的矩阵迷宫,从上到下依次给行编号为 \(0,1,...,n-1\), 从左到右依次给列编号为 \(0,1,2...,m-1\)。 游戏规则是: 从起点出发,每步操作可以移动到上、下、左、右四个方向的空地上,直到终点。

游戏中,从起点到终点并不一定会有直接的通路, 玩家最多可以使用一次穿梭技能,: 从一块空地移动到距离至多为 \(d\) 另一片空地上(起点和终点也视为空地),无论中间是否有障碍。该技能使用时计为一步操作。

询问最少需要多少步才能到达终点,并给出一个方案(移动步骤)。

输入描述:

第一行 \(3\) 个整数, \(n,m,d\), \((1\le n,m\le2000,\,0\le d\le2000)\), 分别表示行数,列数,和穿梭的最大距离。

接下来 \(n\) 行,每行一个长度为 \(m\) 的字符串,表示地图,其中 \(S\) 为起点, \(T\) 为终点, \(‘.‘\) 为空地, \(\rm ‘X‘\) 为障碍, 输入保证有且仅有一个起点和一个终点。

输出描述:

第一行输出一个整数 \(t\) 表示最少所需要的步骤。

接下来 \(t+1\) 行, 每行输出两个整数 \(x_i,y_i\),中间以空格分隔, 表示每一步所经过的坐标。其中,第一行和最后一行应分别对应起点和终点。

特别地,如果没有可以走到终点的方案, 则再一行输出 \(-1\)

答案不唯一, 符合要求即可。

大致做法:

先对起点和终点分别做一次 \(bfs\), 然后对终点(起点)处理结果, 做长度为 \(d\times d\) 的二维滑窗处理, 然后遍历起点(终点)处理结果, 以当前点为中心的四方四个窗口取最小值, 最小值+起点(终点)的数据就是当前点的最优步数, 然后对所有结果取最小值。时间复杂度是 \(\mathcal{O(n\times m)}\)

当时没想到二维滑窗,只会一维滑窗, 所以傻傻用了二维线段树去做,??\(\mathcal{O(n\times m \times\log n\times\log m)}\), 太蠢了,只能过 \(70\%\) 的数据。

\(code:\)

#pragma GCC optimize("-O2")
#include<bits/stdc++.h>
#define min(a,b) (a)<(b)?(a):(b)
#define max(a,b) (a)>(b)?(a):(b)
#define reg register
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=2e3+5;
const double ep=1e-8;

void read(int&x)
{
    char c;
    while(!isdigit(c=getchar()));x=c-‘0‘;
    while(isdigit(c=getchar()))x=(x<<1)+(x<<3)+c-‘0‘;
}
void write(int x)
{
    if(x<0)putchar(‘-‘),x=-x;
    if(x>=10)write(x/10);
    putchar(x%10+‘0‘);
}

char s[maxn][maxn];
int sx,sy,ex,ey,n,m,dis;
int en[maxn][maxn],sn[maxn][maxn];
int d[4][2]={-1,0,0,1,1,0,0,-1};
struct P{
    int x,y;
};
queue<P>que;
P las[maxn][maxn],lae[maxn][maxn];
void bfs1()
{
    memset(en,inf,sizeof(en));
    en[ex][ey]=0;que.push({ex,ey});
    reg P u;reg int tx,ty;
    while(!que.empty())
    {
        u=que.front();que.pop();
        for(auto f:d)
        {
            tx=u.x+f[0];ty=u.y+f[1];
            if(s[tx][ty]==‘X‘||en[tx][ty]<=en[u.x][u.y]+1||tx<1||ty<1||tx>n||ty>m)continue;
            en[tx][ty]=en[u.x][u.y]+1;lae[tx][ty]={u.x,u.y};
            if(tx==sx&&ty==sy)return;
            que.push({tx,ty});
        }
    }
}
void bfs2()
{
    while(!que.empty())que.pop();
    memset(sn,inf,sizeof(sn));
    sn[sx][sy]=0;que.push({sx,sy});
    reg P u;reg int tx,ty;
    while(!que.empty())
    {
        u=que.front();que.pop();
        for(auto f:d)
        {
            tx=u.x+f[0];ty=u.y+f[1];
            if(s[tx][ty]==‘X‘||sn[tx][ty]<=sn[u.x][u.y]+1||tx==sx&&ty==sy||tx<1||ty<1||tx>n||ty>m)continue;
            sn[tx][ty]=sn[u.x][u.y]+1;las[tx][ty]={u.x,u.y};
            if(tx==ex&&ty==ey)return;
            que.push({tx,ty});
        }
    }
}
P sta[maxn*maxn];int cnt;
struct Q {
    int v,x,y;
    bool operator<(const Q&q)const{return v<q.v;}
}q[maxn],qq,a[maxn][maxn],b[maxn][maxn];
inline Q getmin(int x,int y)
{
    int x1=max(1,x-dis),y1=max(1,y-dis);
    return min(min(b[x1][y1],b[x1][y]),min(b[x][y1],b[x][y]));
}
int main()
{
    read(n);read(m);read(dis);
    for(reg int i=1;i<=n;i++)
    {
        gets(s[i]+1);
        for(int j=1;j<=m;j++)
            if(s[i][j]==‘S‘)sx=i,sy=j;
            else if(s[i][j]==‘T‘)ex=i,ey=j;
    }
    bfs1();bfs2();
    for(int i=1,l,r,up=min(dis+1,m);i<=n;i++)
    {
        l=1,r=0;
        for(int j=1;j<=up;j++)
        {
            while(r&&q[r].v>=en[i][j])r--;
            q[++r]={en[i][j],i,j};
        }
        a[i][1]=q[l];
        for(int j=2;j<=m;j++)
        {
            while(q[l].y<j&&l<=r)l++;
            if(j+dis<=m)
            {
                while(r>=l&&q[r].v>=en[i][j+dis])r--;
                q[++r]={en[i][j+dis],i,j+dis};

            }
            a[i][j]=q[l];
        }
    }
    for(int j=1,l,r,up=min(dis+1,n);j<=m;j++)
    {
        l=1,r=0;
        for(int i=1;i<=up;i++)
        {
            while(r&&q[r].v>=a[i][j].v)r--;
            q[++r]={a[i][j].v,i,a[i][j].y};
        }
        b[1][j]=q[l];
        for(int i=2;i<=n;i++)
        {
            while(q[l].x<i&&l<=r)l++;
            if(i+dis<=n)
            {
                while(r>=l&&q[r].v>=a[i+dis][j].v)r--;
                q[++r]={a[i+dis][j].v,i+dis,a[i+dis][j].y};
            }
            b[i][j]=q[l];
        }
    }
    P ss,ee;int ans=inf;
    if(en[sx][sy]!=inf)
    {
        ans=en[sx][sy];ss={ex,ey};ee={sx,sy};
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            qq=getmin(i,j);
            if(sn[i][j]<inf&&ans>sn[i][j]+qq.v+1)
            {
                ans=sn[i][j]+qq.v+1;
                ss={i,j};
                ee={qq.x,qq.y};
            }
        }
    }
    write(ans>=inf?-1:ans);putchar(10);
    if(ans>=inf)return 0;
    ans++;
    if(ee.x==sx&&ee.y==sy)
    {
        while(ans)
        {
            write(ee.x-1);putchar(32);write(ee.y-1);putchar(10);
            ee=lae[ee.x][ee.y];ans--;
        }
        return 0;
    }
    while(1)
    {
        sta[++cnt]=ss;
        if(ss.x==sx&&ss.y==sy)break;
        ss=las[ss.x][ss.y];
    }
    while(cnt)
    {
        write(sta[cnt].x-1);putchar(32);write(sta[cnt].y-1);putchar(10);
        ans--;cnt--;
    }
    while(ans)
    {
        write(ee.x-1);putchar(32);write(ee.y-1);putchar(10);
        ee=lae[ee.x][ee.y];ans--;
    }
}

【数据结构】k维滑窗:扩展到k维的单调队列

标签:友谊   sdi   遍历   block   分割   queue   turn   char   line   

原文地址:https://www.cnblogs.com/kkkek/p/12799358.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!