Y1课程笔记:第四课时(线性数据结构&&搜索)

PART1

知识点一:ST表

献上例题

找高楼

时间限制:1秒        内存限制:128M

题目描述

每当黄昏,拔地而起的高楼大厦在灿烂的余晖下似一个个巍峨的巨人,两排立交桥直升到天,错落有致的商店、写字楼、居民房……鳞次栉比,巧夺天工。小可看着城市中的高楼大厦,感觉到非常的神奇。他将N座高楼从1到N进行了编号,并且测量出来了每个楼的高度。

达达等M个人突然想问小可:从A号楼到B号楼最高的楼和最低的楼的差是多少?

输入描述

第一行输入包含两个正整数N和M,含义如题目所示。

第二行输入N个整数,含义如题目所示。

第三行开始输入M行数据,每行包含两个数字A和B,含义如题目所示。

输出描述

输出包含M行,每行输出对应询问的答案。

样例

输入

5 2
3 1 5 2 7
2 3
1 5

输出

4
6

提示

对于30%的数据,N<=1000,M<=2000,每座楼高度h<=1000

对于100%的数据,N<=50000,M<=200000,每座楼高度h<=1000000

所有数据保证1<=A<=B<=N

解题思路

此题的数据量达到了50000,如果采用枚举必定会超时,因此我们引入st表。

//知识补充

ST表这种数据结构可以用来解决可重复贡献问题,基于倍增思想,可以做到O(nlogn) 预处理,O(1) 查询。

//

以此题中最大值为例,设f[i][j]为从i开始长度为2^j的序列中的最大值,先将每个f[i][0]初始化为a[i],

运用倍增思想,我们很容易推出在递推中,f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1]),在查询时,设k=floor(log2(r-l+1)),则在l-r区间中最大值为max(f[l][k],f[r-po[k]+1][k]),最小值同理。

AC代码

#include<bits/stdc++.h>
using namespace std;
int m,n,a[50005],ff[50005][105],f[50005][105],po[105]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288};
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
	    cin>>a[i];
	    f[i][0]=a[i];
	    ff[i][0]=a[i];
	}
	for(int j=1;po[j]<=n;j++){
	    for(int i=1;i+po[j]-1<=n;i++){
	        f[i][j]=max(f[i][j-1],f[i+po[j-1]][j-1]);
	        ff[i][j]=min(ff[i][j-1],ff[i+po[j-1]][j-1]);
	        //cout<<f[i][j]<<' '<<ff[i][j]<<endl;
	    }
	}
	int k,ans1,ans2,l,r;
	for(int i=1;i<=m;i++){
        cin>>l>>r;
        k=floor(log2(r-l+1));
        ans1=max(f[l][k],f[r-po[k]+1][k]);
        ans2=min(ff[l][k],ff[r-po[k]+1][k]);
        cout<<ans1-ans2<<endl;
	}
    //k=log2(r-l+1),ans=max(f[l][k],f[r-2^k+1,k]) 
}

PART2

知识点1:搜索剪枝

剪枝:通过某种判断,避免一些不必要的遍历过程。搜索的时间复杂度通常很大,通过剪枝对搜索进行优化,可以大大提高程序运行的效率。

先来个简单的

数的划分

时间限制:1秒        内存限制:128M

题目描述

将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序)。 例如:n=7,k=3,下面三种分法被认为是相同的。 1,1,5;  1,5,1;  5,1,1; 问有多少种不同的分法。

输入描述

输入n,k  (6< n< =200,2< =k< =6)

输出描述

一个整数,即不同的分法。

样例

输入

7 3

输出

4

解题思路

想暴力搜索是一定会超时的,因此考虑剪枝策略:

1.题目中说明了组合相同、顺序不同为一种情况,因此我们可以在搜索时维护序列单调不减性。

2.由1可推出当(已经选定的数的和+上一个数*(k-已经选定的数的数量+1))>n时(即即使后面每一个都选最小的也凑不出n),可以剪枝。

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,k,cnt;
void dfs(int sum,int p,int ma){
    if(p==k){
        if(n-sum>=ma){
            cnt++;
        }
        return;
    }
    if(sum>=n){
        return;
    }
    if(sum+ma*(k-p+1)>n){
        return;
    }    
    if(sum+ma*(k-p+1)==n){
        cnt++;
        return;
    } 
    for(int i=ma;i<=n-sum;i++){
        dfs(i+sum,p+1,i);
    }
}
int main(){
    ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k;
	dfs(0,1,1);
	cout<<cnt;
}

题目

从上到下

时间限制:1秒        内存限制:128M

小可在玩一个格子游戏。格子游戏的地图是一个大正方形,大正方形被分为了 n\times nn×n 个小正方形。小可一开始位于左上角的格子,要走到左下角的格子上去。走格子的路径有一个规则,必须路过所有格子恰好一次。如 n=3n=3 时有两种走法:


现在小可想知道给定 nn ,有多少条可行路径
输入描述

一行,一个整数 nn

输出描述

一行,一个整数,表示可行路径数

输入样例

3
输出样例

2
数据范围

1<=n<=7

解题思路

深搜枚举每一种路线,判断剪枝

剪枝1

将地图一分为二,走了就一定无法走完,当上下能走,左右走不了

剪枝2

进入死路,当一个点周围有2个及以上必经点时,一定会寄,直接return

AC代码

#include<bits/stdc++.h>
using namespace std;
int n,ans,a[10][10],dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int getfree(int x,int y){//判断一个点周围有几个已经点
    int cnt=0;
    for(int i=0;i<4;i++){
        int xx=x+dx[i],yy=y+dy[i];
        if(a[xx][yy]==0){
            cnt++;
        }
    }
    return cnt;
}
void dfs(int x,int y,int step){
    if(x==n&&y==1){//走完
        if(step==n*n){
            ans++;
        }
        return;
    }
    if((a[x][y-1]&&a[x][y+1]&&!a[x-1][y]&&!a[x+1][y])||(!a[x][y-1]&&!a[x][y+1]&&a[x-1][y]&&a[x+1][y])){//第一种死路情况
        return;
    }
    int cntf=0,nx,ny;
    for(int i=0;i<4;i++){//第二种死路情况
        int xx=x+dx[i],yy=y+dy[i];
        if(a[xx][yy]||(xx==n&&yy==1)){
            continue;
            
        }
        int f=getfree(xx,yy);
        if(f==1){
            cntf++;
            nx=xx;
            ny=yy;
        }
    }
    if(cntf>1){
        return;
    }
    else{
        if(cntf==1){
            a[nx][ny]=1;
            dfs(nx,ny,step+1);
            a[nx][ny]=0;
        }
    else{for(int i=0;i<4;i++){//继续搜
        int xx=x+dx[i],yy=y+dy[i];
        if(a[xx][yy]==0){
        a[xx][yy]=1;
            dfs(xx,yy,step+1);
            a[xx][yy]=0;
        }
    }
    }
    }
}
int main(){
    cin>>n;
    for(int i=0;i<=n+1;i++){
        a[0][i]=a[n+1][i]=a[i][0]=a[i][n+1]=1;
    }
    a[1][1]=1;
    dfs(1,1,1);
    cout<<ans;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值