C++记忆化搜索是一种优化算法,用于加快递归函数的执行速度。该方法通过保存已计算的结果,避免重复计算以提高程序性能。
先看题目:滑雪
给定一个 R 行 C 列的矩阵,表示一个矩形网格滑雪场。
矩阵中第 i 行第 j 列的点表示滑雪场的第 i 行第 j 列区域的高度。
一个人从滑雪场中的某个区域内出发,每次可以向上下左右任意一个方向滑动一个单位距离。
当然,一个人能够滑动到某相邻区域的前提是该区域的高度低于自己目前所在区域的高度。
下面给出一个矩阵作为例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
在给定矩阵中,一条可行的滑行轨迹为 24−17−2−1。
在给定矩阵中,最长的滑行轨迹为 25−24−23−…−3−2−1,沿途共经过 25 个区域。
现在给定你一个二维矩阵表示滑雪场各区域的高度,请你找出在该滑雪场中能够完成的最长滑雪轨迹,并输出其长度(可经过最大区域数)。
输入格式
第一行包含两个整数 R 和 C。
接下来 R 行,每行包含 C 个整数,表示完整的二维矩阵。
输出格式
输出一个整数,表示可完成的最长滑雪长度。
数据范围
1≤R,C≤300,
0≤矩阵中整数≤10000
输入样例:
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
输出样例
25
先看代码
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 310;
int n,m;
int g[N][N];
int f[N][N];
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
int dp(int x,int y)
{
int &v=f[x][y];
if(v != -1) return v;
v=1;
for(int i=0;i<4;i++)
{
int a=dx[i]+x,b=dy[i]+y;
if(a>=0&&a<n&&b>=0&&b<m&&g[a][b]<g[x][y])
{
v=max(v,dp(a,b)+1);
}
}
return v;
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
cin>>g[i][j];
}
}
memset(f,-1,sizeof f);
int res = 0;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
res=max(res,dp(i,j));
}
}
cout<<res<<endl;
return 0;
}
解析:
什么时候用到记忆化搜索呢?
在一个图中,而且没有形成一个环。也相当于是可以拓扑排序,这时要我们求从一个点为起点,可以逐渐下降的步数的最大值
1、储存图,因为这里不是一个数的形状,所以矩阵存储图
2、初始化f[N][N]
3、得到当前点的数值,然后判断,要是到达最后,就回溯
4、用偏移量来判断方向性,并且得到最大值
题目:电
某城市有 N 个电力节点,编号 1∼N。
这些电力节点形成的电力网络,可以看作一个 N 个节点 N−1 条边的连通图。
每个电力节点都有一个固定的电容,其中第 i 个节点的电容为 Ai。
现在,可以选择其中一个节点进行供电,其它节点也可以根据实际连接以及具体电容情况接收电力。
具体来说,如果第 i 个节点通电,那么它也可以将电力传输给其它所有与它直接连接且电容严格小于 Ai 的节点。
我们希望通过合理选择初始供电节点,从而使得尽可能多的节点能够通电。
请你计算并输出可以通电的最大节点数量。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 N。
第二行包含 N 个整数 A1,A2,…,AN。
接下来 N−1 行,每行包含两个整数 Xi,Yi,表示节点 Xi 和 Yi 之间存在直接连接。
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y,其中 x 为组别编号(从 1 开始),y 为可以通电的最大节点数量。
数据范围
1≤T≤1001≤T≤100,
1≤Ai≤10^9,
1≤Xi,Yi≤N
一个测试点内最多 15 组数据满足 1≤N≤2×105,其余数据满足 1≤N≤103。
输入样例:
2
5
1 2 3 4 3
1 3
2 3
4 3
4 5
6
1 2 3 3 1 4
3 1
3 2
3 4
4 5
1 6
输出样例:
Case #1: 5
Case #2: 3
代码:
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2e5+10,M=2*N;
int n;
int h[N],e[M],ne[M],idx;
int g[N];
int f[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int dp(int u)
{
if(f[u]!=-1) return f[u];
int res = 1;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(g[u]>g[j]) res +=dp(j);
}
f[u]=res;
return res;
}
int main()
{
int T;
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) cin>>g[i];
memset(h,-1,(n+1)*4);
idx=0;
for(int i=1;i<=n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
memset(f,-1,(n+1)*4);
int res = 0;
for(int i=1;i<=n;i++)
{
res=max(res,dp(i));
}
printf("Case #%d: %d\n",t,res);
}
return 0;
}
1、这里的图形是数的样子,所以我们可以使用邻接表储存,不确定方向,所以要相互联通
2、g[N]数组储存点的数值,初始化,因为只有一部分数据是接近最大值的,所以我们这样可以减少时间
3、如果遍历到最后了,则返回答案
4、开始遍历,如果找到自己的子节点的数值比自己小,则向下传递,覆盖当前数值
5、返回最终答案