前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如果把本就让你抓耳挠腮的“一维局部最大值”推广到“二维”

如果把本就让你抓耳挠腮的“一维局部最大值”推广到“二维”

作者头像
ACM算法日常
发布2021-07-16 16:19:08
3060
发布2021-07-16 16:19:08
举报
文章被收录于专栏:ACM算法日常

我相信大部分人都见过这么一个题目:“寻找任意一个一维数组中的局部最大值”,无论是在算法课上还是在 Leetcode 上,并为了理解讲解而抓耳挠腮。

笔者

笔者曾获得 ICPC 2020 世界总决赛资格,ICPC 2020 亚洲区域总决赛第五名。

引入

题目描述

峰值元素是指其值大于左右相邻值的元素。给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设

nums[-1] = nums[n] = \infty

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-peak-element

分析

代码语言:javascript
复制
void solve(int l,int r)
{
    int mid=l+r>>1;
    if (nums[mid]>num[mid+1]) return solve(l,mid);
    return solve(mid+1,r);
}

int main()
{
    int ans=solve(0,n-1);
}

二维

二维这个问题,笔者最早是在 Stanford 的某次算法课作业中看到:

题目描述

分析

看起来正规的做法
代码语言:javascript
复制
int n,a[1010][1010];

int query(int x,int y)
{
    if (a[x][y]>0) return a[x][y];
    printf("? %d %d\n",x-1,y-1);
    fflush(stdout);
    int z;
    scanf("%d",&z);
    a[x][y]=z;
    return z;
}

void solve(int rl,int rr,int cl,int cr)
{
    if (rr==rl)  {printf("! %d %d\n",rr-1,cr-1);return ;}
    if (rr-rl==1)
    {
        int a=query(rl,cl),b=query(rl,cl+1),c=query(rl+1,cl),d=query(rl+1,cl+1);
        if (a>b&&a>c) {printf("! %d %d\n",rl-1,cl-1);return ;}
        if (b>a&&b>d) {printf("! %d %d\n",rl-1,cl);return ;}
        if (c>a&&c>d) {printf("! %d %d\n",rl,cl-1);return ;}
        printf("! %d %d\n",rl,cl);return ;
    }
    int rmid=rl+rr>>1,cmid=cl+cr>>1;
    int Min=0,Minr,Minc,t;
    for (int i=rl;i<=rr;i++)
    {
        t=query(i,cl);
        if (t>Min) Min=t,Minr=i,Minc=cl;
        t=query(i,cr);
        if (t>Min) Min=t,Minr=i,Minc=cr;
        t=query(i,cmid);
        if (t>Min) Min=t,Minr=i,Minc=cmid;
    }
    for (int i=cl+1;i<cr;i++)
    {
        t=query(rl,i);
        if (t>Min) Min=t,Minr=rl,Minc=i;
        t=query(rr,i);
        if (t>Min) Min=t,Minr=rr,Minc=i;
        t=query(rmid,i);
        if (t>Min) Min=t,Minr=rmid,Minc=i;
    }
    int wx=-1,wy,w=0;
    if (Minr-1>=rl)
    {
        int t=query(Minr-1,Minc);
        if (t>Min&&t>w) w=t,wx=Minr-1,wy=Minc;
    }
    if (Minr+1<=rr)
    {
        int t=query(Minr+1,Minc);
        if (t>Min&&t>w) w=t,wx=Minr+1,wy=Minc;
    }
    if (Minc-1>=cl)
    {
        int t=query(Minr,Minc-1);
        if (t>Min&&t>w) w=t,wx=Minr,wy=Minc-1;
    }
    if (Minc+1<=cr)
    {
        int t=query(Minr,Minc+1);
        if (t>Min&&t>w) w=t,wx=Minr,wy=Minc+1;
    }
    if (wx==-1) {printf("! %d %d\n",Minr-1,Minc-1);return ;}
    if (wx<rmid&&wy<cmid) solve(rl,rmid-1,cl,cmid-1);
    else if (wx>rmid&&wy<cmid) solve(rmid+1,rr,cl,cmid-1);
    else if (wx<rmid&&wy>cmid) solve(rl,rmid-1,cmid+1,cr);
    else solve(rmid+1,rr,cmid+1,cr);
}

int main()
{
    memset(a,0,sizeof a);
    scanf("%d",&n);
    solve(1,n,1,n);
    fflush(stdout);
    return 0;
}

这种解法的正确性已经在上面的证明中得到了保证,我们考虑这种解法的时间复杂度,显然首次需要询问 6n 个位置,而之后每一次子矩阵中均只需要询问中间行和中间列的元素(因为其周围的边界在上一次询问中已经完成,可以重复使用询问结果),所以询问次数的上限是 8n ,即使用此方法解决问题的时间复杂度是O(n)。

随机做法
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ACM算法日常 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 笔者
  • 引入
    • 题目描述
      • 分析
      • 二维
        • 题目描述
          • 分析
            • 看起来正规的做法
            • 随机做法
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档