题目描述
题意翻译
给出一个 N 层的方格金字塔,自顶向下依次标号为第 1 到第 N 层。
其中第 i(1≤i≤N) 层有 2i−1 个方格。(具体形态见下面的图)
第 N 层有一个 1 到 2N−1 的排列,其他层的数字按以下规则生成:方格 b 中填写的整数,是方格 b 正下方、左下方和右下方方格中所写整数的中位数。
现在给出第 N 层的数字,请你求第一层的数字。
翻译提供者:WAAutoMaton
输入输出样例
输入 #1复制
4 1 6 3 7 4 5 2
输出 #1复制
4
输入 #2复制
2 1 2 3
输出 #2复制
2
解析:
我们如果用暴力的方法进行遍历时,时间复杂度为O((2*n-1)*n ) 大约 O(n*n)爆
这是需要进行优化,我们只是找一个数,虽然这时数不确定,我们可以假设这个数。
猜数一般使用二分法。
用 0 1 来代表: 如果 为 1 ,则 比猜的数 大 ,否者比猜的数小。
这时就是一个 0 1表示的金字塔了。
我们画图可以知道当越接近中间时,相邻的两 个 1 或者 2 个 0被使用的次数最多,且最不容易被帖边的数删除掉。
思路:
从数组的中间先两边遍历,如果 有两 个 连续的 1 时,这时候猜的数 是小 的,二分向右端点靠近。
如果有两个连续的 0先出现,这个数 猜大了,二分向左端点靠近。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010;
int a[N];
int n;
int ans;
inline bool up(int x,int y,int mid)
{
return a[x] > mid && a[y] >mid;
}
inline bool down(int x,int y,int mid)
{
return a[x]<= mid && a[y]<=mid;
}
inline bool check(int mid)
{
for(int i = 0;i < n-1;i++)//从中间 开始 遍历。
{
if(up(n-i,n-i-1,mid) || up(n+i,n+i+1,mid)) //就是 mid 先右端点的数靠近,猜的数小了
{
return 0;
}
if(down(n-i,n-i-1,mid) || down(n+i,n+i+1,mid)) // 猜的数大了
{
return 1;
}
}
return down(1,1,mid);//如果时 01 交替出现时,只要查看那个 数 比较 多 ,直接 查询第一个数 即可
}
//
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n*2-1;i++)
{
scanf("%d",&a[i]);
}
int l= 0,r = n*2 -1;
while(l <= r)
{
int mid = (l+r)>>1;
if(check(mid)){
r = mid-1;
ans =mid;
}else{
l = mid +1;
}
}
printf("%d\n",ans);
return 0;
}
时间复杂度:O(logn * n)