DAY3 倍增学习报告

DAY3 倍增学习报告

没学明白就完事了,报什么告 这天的题虽然就4道且2道都是模板,但是由于学的太一般了,一道题琢磨了一下午,故认真消化了一下,复习复习,另一道等明天讲完了补做。

/(课堂笔记)
倍增是将一般循环转化成找2的n次方,时间O(logn)。
ST表利用倍增能很快解决查询区间最大/最小值问题。 /

本报告唯一的内容,是通过下面这个问题(又是一个我玩了一下午的题)理解一下ST表的运用:(P2216)
在这里插入图片描述
一句话题解:此题是一个二维的ST表 (玩ST表熟练的同志就不用再看下去了)
ST表的基本原理是,用一个DP数组存储从第i个数开始,包括它在内的2^j个数的和/最大值/最小值等基本特征,然后对这个DP数组进行查询。
首先,回顾一下ST表的模板:

#include<cstdio>
#include<algorithm>
using namespace std;
long long d[100001],c[100001],dp[100001][101],st[100001];
int main(){
	long long n,m,i,j,k = 0,l,r,temp;
	scanf("%lld %lld",&n,&m);
	for(i = 1;i <= n;i++){
		scanf("%lld %lld",&d[i],&c[i]);
		dp[i][0] = c[i];
	}
	for(j = 1;(1 << j) <= n;j++){
		for(i = 1;i + (1 << j) - 1 <= n;i++){
			dp[i][j] = max(dp[i][j - 1],dp[i + (1 << (j - 1))][j - 1]);
		}
	}
	for(i = 1;i <= n;i++){
		if((1 << k) <= i) k++;
		st[i] = k - 1;
	}
	for(i = 1;i <= m;i++){
		scanf("%lld %lld",&l,&r);
		temp = st[r - l + 1];
		printf("%lld\n",max(dp[l][temp],dp[r - (1 << temp) + 1][temp]));
	}
	return 0;
} 

这里比较值得一提的是,此处st存储的是不大于区间长的最大的2^j的j值,用于保证一次查询一定能查到最大值。

接下来看这道题。
参考代码1:(从洛谷上学的)

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int a[1001][1001],b[1001][1001],c[1001][1001],temp,k;
int query(int x,int y){
	int ret1,ret2;
	ret1 = max(b[x][y],max(b[x + k - (1 << temp)][y],max(b[x][y + k - (1 << temp)],b[x + k - (1 << temp) + 1][y + k - (1 << temp)])));
	ret2 = min(c[x][y],min(c[x + k - (1 << temp)][y],min(c[x][y + k - (1 << temp)],c[x + k - (1 << temp) + 1][y + k - (1 << temp)])));
	return ret1 - ret2;
}
int main(){
	int n,m,i,j,l,res = 2147483647;
	scanf("%d %d %d",&n,&m,&k);
	for(i = 1;i <= n;i++){
		for(j = 1;j <= m;j++){
			scanf("%d",&a[i][j]);
			b[i][j] = c[i][j] = a[i][j];
		}
	}
	temp = log2(k);
	for(l = 0;l < temp;l++){
		for(i = 1;i + (1 << l) <= n;i++){
			for(j = 1;j + (1 << l) <= m;j++){
				b[i][j] = max(b[i][j],max(b[i + (1 << l)][j],max(b[i][j + (1 << l)],b[i + (1 << l)][j + (1 << l)])));
				c[i][j] = min(c[i][j],min(c[i + (1 << l)][j],min(c[i][j + (1 << l)],c[i + (1 << l)][j + (1 << l)])));
			}
		}
	}
	for(i = 1;i <= n - k + 1;i++){
		for(j = 1;j <= m - k + 1;j++){
			res = min(res,query(i,j));
		}
	}
	printf("%d",res);
	return 0;
}

我并不会科学的解释这个代码,只能简要的解释一下,如解读有误请指正
这个采取的是ST表四合一的处理方式,b、c都表示从(i,j)开始k为边长中的最值,先赋值为a,然后再初始处理表示最值,最后按ST表的方式进行查询并合并出结果,只不过查询的次数是二维的。

参考代码2:

#include<cstdio>
#include<algorithm>
using namespace std;
int ma[1001][1001][10],mi[1001][1001][10];
int st[1001],a,b,n,data;
int main(){
	int i,j,k,x,y,s;
	scanf("%d %d %d",&a,&b,&n);
	for(i = 1;i <= a;i++){
		for(j = 1;j <= b;j++){
	  		scanf("%d",&data);
	  		ma[i][j][0]=mi[i][j][0]=data;
		}
	}
	st[0] = -1;
	for(i = 1;i <= max(a,b);i++) st[i] = st[i >> 1] + 1;
	for(i = 1;i <= a;i++){
		for(k = 1;k <= 10;k++){
	    	for(j = 1;j + (1 << k) - 1 <= b;j++){
	    		ma[i][j][k]=max(ma[i][j][k - 1],ma[i][j + (1 << k - 1)][k - 1]);
	    		mi[i][j][k]=min(mi[i][j][k - 1],mi[i][j + (1 << k - 1)][k - 1]);
	    	}//向右的ST表
		}
	}
    for(i = 1;i <= a;i++){
    	for(j = 1;j <= b;j++){
      		x = j,y = min(j + n - 1,b);
      		s = st[y - x + 1];
      		ma[i][j][0]=max(ma[i][x][s],ma[i][y-(1<<s)+1][s]);
      		mi[i][j][0]=min(mi[i][x][s],mi[i][y-(1<<s)+1][s]);
      	}//将向下的ST表初状态赋值为向右的ST表
    }
    for(j = 1;j <= b;j++){
    	for(k = 1;k <= 10;k++){
        	for(i = 1;i + (1 << k) - 1 <= a;i++){
        		ma[i][j][k]=max(ma[i][j][k - 1],ma[i + (1 << k - 1)][j][k - 1]);
        		mi[i][j][k]=min(mi[i][j][k - 1],mi[i + (1 << k - 1)][j][k - 1]);
        	}//合并向下的ST表
        }
    }
    int ans = 2147483647;
    for(i = 1;i + n - 1<= a;i++){
    	for(j = 1;j +n - 1<= b;j++){
      		x = i,y = i + n - 1;
      		s = st[y - x + 1];
      		ans = min(ans,max(ma[x][j][s],ma[y - (1 << s) + 1][j][s])-min(mi[x][j][s],mi[y - (1 << s) + 1][j][s]));
    	}//查询
    }
    printf("%d\n",ans);
    return 0;
}

这一段代码完全是两个一维ST表的合成。首先进行一维ST表的合成,然后再把这个ST表赋值给下一维的初状态,然后进行下一维ST表的合成,就完成了二维ST表,查询方法也与一维ST表相同,只不过需要取n次查询的最小值。这样写比较接近原模板,好理解和模仿,但是空间复杂度较高。

Thank you for reading!

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页