当前位置: 首页 > news >正文

大盗阿福-选和不选思路:状态数组st写法-->新写法DFS-->记忆化-->递推

题目来自DOTCPP:

选和不选的思路:

之前我发布的一篇博客中指数型枚举,用到“选和不选”思路,但是在那个代码中用到了st数组来记录选和不选的情况。这道题目中用st数组来记录选和不选的情况,会超时,并且后续用mem数组记忆化来优化比较麻烦。代码如下:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int n;
int arr[N];       // 每家店铺的现金
int st[N];        // 状态数组:0表示未确定,1表示洗劫,2表示不洗劫
int max_cash;     // 最大现金

// DFS函数
void dfs(int x) {
    // 递归终止条件:枚举完所有店铺
    if (x > n) {
        int total = 0;
        for (int i = 1; i <= n; i++) {
            if (st[i] == 1) {
                total += arr[i];
            }
        }
        // 更新最大现金
        max_cash = max(max_cash, total);
        return;
    }

    // 洗劫当前店铺
    st[x] = 1;
    dfs(x + 2); // 不能洗劫相邻店铺,所以跳到 x+2
    st[x] = 0;  // 回溯

    // 不洗劫当前店铺
    st[x] = 2;
    dfs(x + 1); // 跳到下一家店铺
    st[x] = 0;  // 回溯
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
        }
        // 初始化
        max_cash = 0;
        memset(st, 0, sizeof(st));
        // 调用DFS函数
        dfs(1);
        // 输出结果
        printf("%d\n", max_cash);
    }
    return 0;
}

上面代码为什么不好优化?难点在于每个递归分支的st状态不同,导致mem数组的索引x不好缓存结果。因此,我在通常会使用另外一种写法,不用状态数组st来记录选和不选的情况,这种情况比较好优化。代码如下:

不使用状态数组st的DFS(超时):

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+10;

int t,n;
int arr[N];

int dfs(int x){
    if(x > n){
        return 0;
    }
    //选
    int res1 = dfs(x+2) + arr[x];
    //不选
    int res2 = dfs(x+1);
    
    return max(res1, res2);
}

signed main(){
    cin >> t;
    while(t--){
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> arr[i];
        int res = dfs(1);
        cout << res << endl;
    }
    return 0;
}

记忆化:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+10;

int t,n;
int arr[N];
//记忆化数组
int mem[N];

int dfs(int x){
    if(x > n){
        return 0;
    }
    if(mem[x]) return mem[x];
    //选
    int res1 = dfs(x+2) + arr[x];
    //不选
    int res2 = dfs(x+1);
    
    mem[x] = max(res1, res2);
    return mem[x];
}

signed main(){
    cin >> t;
    while(t--){
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> arr[i];
        //初始化记忆数组
        memset(mem, 0, sizeof mem);
        int res = dfs(1);
        cout << res << endl;
    }
    return 0;
}

递推:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+10;

int t,n;
int arr[N];
int f[N];

signed main(){
    cin >> t;
    while(t--){
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> arr[i];
        //初始化f数组
        memset(f, 0, sizeof f);
        //从下往上归
        for(int i = n; i >= 1; i--){
            f[i] = max(f[i+2]+arr[i], f[i+1]);
        }
        cout << f[1] << endl;
    }
    return 0;
}

总结:

用到状态数组st的代码,是找到所有的方案,在找到其中的最大值。由于每个递归分支的st状态不同,导致mem数组的索引x不好缓存结果,所以不好记忆化。不适用状态数组st的DFS代码是返回更大值的那个分支,该代码的本质是找到一个价值最多的路径,并且合法。mem数组的索引x是唯一的,方便记忆化。

两种代码的区别,代码一是找到所有可能的方案,然后在找到合法的最大值。代码二是找到一个价值最多的路径,走下去,并且合法。

这道题目为什么不使用状态数组 st ,因为它不好记忆化,不好优化。记忆化的本质是用参数唯一标识子问题,用到状态数组st的代码参数只有x,但实际问题的状态是由状态数组st的“完整”历史决定,因此,参数只有一个x的情况下是无法唯一标识子问题的。所以,如果我们要优化它(使用状态数组st的代码),我们需要加个参数,非常复杂,代码如下:

int dfs(int x, int prev) { // prev=0表示前一家未洗劫,prev=1表示已洗劫
    if (x > n) return 0;
    if (memo[x][prev] != -1) return memo[x][prev];
    int skip = dfs(x+1, 0); // 不洗劫当前店铺
    int take = (prev == 0) ? (arr[x] + dfs(x+1, 1)) : 0; // 前一家未洗劫才能洗劫当前
    return memo[x][prev] = max(skip, take);
}

相关文章:

  • 初一信息科技教程专用抓包软件1.4.2版本
  • 【经验分享】SpringBoot集成WebSocket开发02 之 实现一个基本示例并Spring Bean注入的方式来组织代码
  • 在浏览器中配置vue请求后端的接口地址
  • 剖析sentinel的限流和熔断
  • 虚幻基础:移动组件
  • x012-MSP430F249智能步进电动百叶窗_proteus_光敏电阻_步进电机_仿真
  • 在芯片器件制造中,一氧化氮气体什么会提升栅氧膜层的质量。
  • Ubuntu 优化 Vim 指南
  • 【GPT入门】第17课 RAG向量检索分类、原理与优化
  • InfluxDB写入测试
  • 贪心算法——c#
  • Ubuntu24.04下管理自己的ssh连接
  • 关于ISP Pipeline LSC(镜头阴影校正)位置的一些想法
  • Java 大视界 -- 基于 Java 的大数据实时流处理中的窗口操作与时间语义详解(135)
  • Elastic Stack 8.16.0 日志收集平台的搭建
  • Java 中的序列化和反序列化是什么?
  • IntelliJ IDEA 快捷键系列:重命名快捷键详解
  • Tailwindcss开启黑夜模式
  • 数据结构-----初始数据结构、及GDB调试
  • python爬虫Scrapy(6)之增量式
  • 苏州建设企业网站/重庆seo杨洋
  • 网站怎么做让PC和手机自动识别/网站接广告
  • 亚马逊网站建设案例/独立站seo是什么
  • 惠州网站建设米普可思/黄石市seo关键词优化怎么做
  • seo对网站的重要性/宁波seo网站
  • 做网站的挣钱么/seo怎么优化简述