Cpp环境【SDUT1128】【Code[VS]1809】【CQYZOJ1823】河床

来源:互联网 发布:网络电玩游戏平台 编辑:程序博客网 时间:2024/05/12 01:55
【问题描述】  

  地理学家们经常要对一段河流进行测量分析。他们从上游开始向下游方向等距离地选择n(n<=30000)个点测量水位深度。得到一组数据a1,a2,…,an,回到实验室后数据分析员根据需要对数据进行分析,发掘隐藏在数据背后的规律。

  最近,乌龙博士发现某种水文现象与河床地势有关,于是他指示分析员要找出一段河流中最大高低起伏差不超过K(1<=K<=100)的最长的一段。这看似一个简单的问题,由于任务紧急,分析员来求助于你,并告诉你博士的所有数据都精确到个位。

【输入格式】  

  第一行包含两个整数N和K,分别表示测量点的个数和博士要求的最大水深度(也就是河床的地势差)。
  第二行包含N个整数,表示从上游开始依次得到的水位深度di(1<=i<=n,0<=di<=32767)

【输出格式】  

  一个整数m,表示最长一段起伏不超过K的河流长度,用测量点的个数表示

【输入样例】  

6 2
5 3 2 2 4 5

【输出样例】  

4

【数据范围】  

30%的数据,满足:1<=n<=5000;
50%的数据,满足:1<=n<=30000,符合条件的最长连续序列长度不超过100;
100%的数据,满足:1<=n<=30000,符合条件的最长连续序列长度不超过n;

【来源】    

从第2个测量点到第5个测量点之间的一段,即3 2 2 4,起伏最大为2。

【原题传送矩阵】

山东理工大学ACM题库 1128
Code[VS]题库 1809 传送矩阵
CQYZOJ 1823 传送矩阵

【思路梳理】

  看到网上很多人的题解都是交的暴力搜索代码,直接从1~n来枚举起点,看此时能够实现的最长长度。算法是没有任何问题的,但是有追求的ACMer不应该止步于此(滑稽脸)。笔者此处提供更为高效的滑动窗口算法,供读者们茶余饭后笑谈。
  简单整理一下可以将题目简化成如下的形式:在线性序列上,求最长的一段,使得该段中最大值与最小值的差不超过一个给定定值k。线段的最大长度是我们所求值。典型的变长滑动窗口问题。
  设置两个滑动窗口进行存储,一个最大队,一个最小队。不断地将线性序列的元素加入到两个滑动窗口中,直到最大队队首highest与最小队队首元素lowest的差值超过了给定值k。
  此时,我们就确定一下highest与lowest的相对位置。为了保证我们剩下来的滑动窗口尽可能的长,那么我们应该让highest与lowest两者当中较靠左边的一个,让它离开滑动窗口,并更新两者。如此反复维护这个滑动窗口,使得highest与lowest的差值不超过k。
  在让新的元素进入滑动窗口以前,更新一次我们的答案ans即可。
  笔者语拙,更多说明详见注释。

【Cpp代码】
#include<queue>#include<cstdio>#include<iostream>#define maxn 30005using namespace std;int n,k,last=1;struct data{int height,id;}a[maxn];//滑动窗口中元素两个属性:距离上游的位置和深度。//一个height用于维护滑动窗口,另一个id用于计算ansstruct cmp1{bool operator()(data a,data b){return a.height<b.height;}};//定义最大队的仿函数struct cmp2{bool operator()(data a,data b){return a.height>b.height;}};//最小队仿函数void solve(){    int ans=1,last=1;//last:当前滑动窗口最左侧元素的id,ans最小值应该是1,只有1个元素是合法解    priority_queue<data,vector<data>,cmp1>q1;    priority_queue<data,vector<data>,cmp2>q2;    q1.push(a[1]);q2.push(a[1]);//值得强调的是,两个滑动窗口维护的是同一段序列!    data highest=q1.top(),lowest=q2.top();    for(int i=2;i<=n;i++)    {        q1.push(a[i]);q2.push(a[i]);        highest=q1.top(),lowest=q2.top();//新元素进入滑动窗口后应该更新这两个元素的值        while(highest.height-lowest.height>k)//当该段序列的起伏差大于了k时        {            if(highest.id<=lowest.id)//最大队队首元素靠左,根据贪心的思想应该使得滑动窗口尽可能长            {                last=max(last,highest.id+1);//注意+1,highest元素是要出滑动窗口的                q1.pop();            }            if(highest.id>lowest.id)//最小队队首元素靠左            {                last=max(last,lowest.id+1);                q2.pop();            }            highest=q1.top();//不忘再次更新            lowest=q2.top();//这一次是可以省略的        }        ans=max(ans,i-last+1);    }    printf("%d",ans);}int main(){//  freopen("in.txt","r",stdin);    scanf("%d%d",&n,&k);    for(int i=1;i<=n;i++)   scanf("%d",&a[i].height),a[i].id=i;    solve();}
0 0
原创粉丝点击