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;
}