c++滑雪以及电(深度理解)

文章讲述了如何使用C++中的记忆化搜索算法来优化递归函数,以解决滑雪轨迹的最长路径问题和电力网络的最大通电节点数问题。在滑雪轨迹问题中,从矩阵中找出最长的下降路径;在电力网络问题中,确定能提供最大电力覆盖的节点。这两个问题都涉及图的遍历和比较节点属性来决定路径或覆盖范围。
摘要由CSDN通过智能技术生成

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、返回最终答案

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值