写一写LCA问题
1.Tarjan
2.ST算法
先列一下思路8
第一步:存数据(X
//链式前向星
void add(int u,int v,int w){
g[cnt].v=v;
g[cnt].w=w;
g[cnt].nxt=head[u];
head[u]=cnt++;}
第二步:准备工作
深度dep(按照遍历顺序);
ver遍历顺序;
dp二维数组(用来存放);
first数组:节点第一次出现的时间;
//u为当前边 d是深度
void dfs(int u,int d){ //
cout<<ver[1]<<" dfa"<<endl;
vis[u]=1;
//tot记录遍历顺序 初始值为0
ver[++tot]=u;
dep[tot]=d;
first[u]=tot;
for(int i=head[u];i!=-1;i=g[i].nxt){
int v=g[i].v;
if(!vis[v]){
dis[v]=dis[u]+g[i].w;
dfs(v,d+1);
//回溯??? ver数组中每个点要出现多次
ver[++tot]=u;
dep[tot]=d; }}}
那么如果我们要求u与v的最近祖先,先找出它们第一次出现的时间区间,然后再在dep中找到那一段深度区间,要找是lca就是这个区间中最小的嘛
(也就是说,在从u到v的这个过程中,经过的最靠近树根的那一个节点。经过这个节点,我们可以从u到v,也可以从v回到u,即可以求v到u的最短路径 dis[u]+dis[v]-2*dis[lca(v,u)])
寻找最小值的过程我们使用rmq来解决,这样更快速
找出这个最浅深度后,我们回到一开始的ver数组中去,按这个序号,找出的那个节点,就是我们要找的lca辣!!
int RMQ(int l,int r){
int k= 0;
while(1<<(k+1)<=r-l+1) k++;
int a=dp[l][k],b=dp[r-(1<<k)+1][k];
return dep[a]<dep[b]?a:b;}
//返回 dep 在 l , r 上的最小值
int LCA(int u,int v){
int x=first[u],y=first[v];
if(x>y)swap(x,y);
int res=RMQ(x,y);
return ver[res]; }
void ST(int n){
for(int i=1;i<=n;i++){
dp[i][0]=i;
}
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
int a=dp[i][j-1],b=dp[i+(1<<(j-1))][j-1];
dp[i][j]=dep[a]<dep[b]?a:b;}}}
*插播一个rmq问题
rmq用dp思想开求一个区间上的最值(这里假设求最小值,最大值同理)
dp[i][j]表示从i位置出发,往后2^j次方个数字的区间上的最值
状态转移方程
dp[i][j]=min(dp[i][j-1],dp[i+2^(j-1) ][ j-1])
*把这个区间从中间剖成等长的两端,每段长为2^(j-1)
1.初始化操作。
void ini(int n){
for(int i=1;i<=n;i++){
dp[i][0]=a[i];
}
//1<<j就是2^j了
for(int j=0;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
2.查询(l,r)上最值
int RMQ(int l,int r){
int k=0;
while(1<<(k+1)<=r-l+1) k++;
//找到最大的k,覆盖 区间
//也可以直接k=log2(r-l+1)-1;
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
一个RMQ例题:Cornfields
题意就是在一个小方块里找出最大最小值 求差
本来是二位RMQ还看见一个巨强的记忆化搜索,不知道会不会超时?
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define N 255
using namespace std;
int dp1[N][N][8][8];
int dp2[N][N][8][8];
int a[N][N];
int n;
int minn(int a,int b,int c,int d){
int m=min(a,b);
int n=min(c,d);
return min(m,n);
}
int maxn(int a,int b,int c,int d){
int m=max(a,b);
int n=max(c,d);
return max(m,n);
}
//这里的dp[r][c][kn][km]指的是以r,c为左上角,2^kn行,2^km列的矩阵中的最值
void st(int n,int m){
int i,j,r,c;
for(i=1;i<=n;i++){
for(j=1;j<=m;j++){
dp1[i][j][0][0]=dp2[i][j][0][0]=a[i][j];
}
}
int kn=(int)(log(double(n))/log(2.0));
int km=(int)(log(double(m))/log(2.0));
for(i=0;i<=kn;i++){
for(j=0;j<=km;j++){
if(!i&&!j)continue;
for(r=1;r+(1<<i)-1<=n;r++){
for(c=1;c+(1<<j)-1<=m;c++){
if(!i){
dp1[r][c][i][j]=min(dp1[r][c][i][j-1],dp1[r][c+(1<<(j-1))][i][j-1]);
dp2[r][c][i][j]=max(dp2[r][c][i][j-1],dp2[r][c+(1<<(j-1))][i][j-1]);
}
else{
dp1[r][c][i][j]=min(dp1[r][c][i-1][j],dp1[r+(1<<(i-1))][c][i-1][j]);
dp2[r][c][i][j]=max(dp2[r][c][i-1][j],dp2[r+(1<<(i-1))][c][i-1][j]);
}
}
}
}
}
}
int RMQ(int x,int y,int x1,int y1){
int l=(int)(log(double(x1-x+1))/log(2.0));
int k=(int)(log(double(y1-y+1))/log(2.0));
return minn(dp1[x][y][l][k],dp1[x1-(1<<l)+1][y][l][k],
dp1[x][y1-(1<<k)+1][l][k],dp1[x1-(1<<l)+1][y1-(1<<k)+1][l][k]);
}
int RMQ2(int x,int y,int x1,int y1){
int l=(int)(log(double(x1-x+1))/log(2.0));
int k=(int)(log(double(y1-y+1))/log(2.0));
return maxn(dp2[x][y][l][k],dp2[x][y1-(1<<l)+1][l][k],
dp2[x1-(1<<l)+1][y][l][k],dp2[x1-(1<<l)+1][y1-(1<<k)+1][l][k]);
}
int main()
{int n, b, k, i, j;
cin >> n >> b >> k;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++){
scanf("%d", &a[i][j]);}
st(n,n);
int u, l;
for (i = 0; i < k; i++)
{
scanf("%d%d", &u, &l);
cout<<RMQ2(u,l,u+b-1,l+b-1)-RMQ(u,l,u+b-1,l+b-1)<<endl;
}
return 0;
}
法二
int c[255][255];//记忆数组
int max=0,min=999999999;
if(!c[x][y]){
for(int l=x;l<=x+b-1;l++){
for(int c=y;c<=y+b-1;c++){
if(a[l][c]<min) min = a[l][c];
if(a[l][c]>max) max = a[l][c];
} }
c[x][y]=max-min;}
cout<<c[x][y]<<endl;