动态规划笔记(四)

博客主要探讨了最长公共子序列和旅行商问题。对于最长公共子序列介绍了暴力回溯递归和记忆化搜索;旅行商问题是求商人不重复访问N个城市的最短路径,属于NP问题,原暴力搜索时间复杂度为O(n!),通过状态压缩增加O(n∗2n)空间后,新时间复杂度为O(n2∗2n),还提及优化技巧。

最长公共子序列

  • 如何暴力回溯递归
int search(int xi,int yi){
    if(xi > n || yi > m){
        return -1;
    }
    if(xi == n && yi == m){
        return 0;
    }
    int a=0,b=0,c=0;
    if(X[xi] = Y[yi]){
     a= search(xi+1,yi+1)+1;   
    }
    b= search(xi,yi+1);
    c= search(xi+1,yi);
    return max(a,b,c);
}
  • 记忆化搜索
int search(int xi,int yi){
    if(xi > n || yi > m){
        return -1;
    }
    if(xi == n && yi == m){
        return 0;
    }
    if(dp[xi][yi]>=0){
        return dp[xi][yi];
    }
    int a=0,b=0,c=0;
    if(X[xi] = Y[yi]){
     a= search(xi+1,yi+1)+1;   
    }
    b= search(xi,yi+1);
    c= search(xi+1,yi);
    dp[xi][yi] = max(a,b,c);
    return dp[xi][yi];
}

旅行商问题

一个商人要不重复地访问N给城市,允许从任意城市出发,在任意城市结束。现已知任意两个城市之间的道路长度。

  • 求城市的访问序列,使得商人走过的路程最短
  • 例:N=4,访问序列3,4,1,2
  • NP问题,最优解无多项式时间算法
  • 时间复杂度?空间复杂度?
  • 状态压缩
    • 时间复杂度
    • 空间复杂度
map[i][j] //i到j距离
bool vis[n] = {false};
int search(int idx,int count){
    if(count==n){
        return 0;
    }
    int min = 1e8;
    for(int i=0;i<n;i++){
        if(!vis[i]){
            vis[i] = true;
            int t = search(i,count+1) + map[idx][i];
            if(t<min){
                min = t;
            }
            vis[i] = false; //复原,假装你没有访问过i(因为下一步i++,访问的就是i+1了)
        }
    }
    return min;
}

时间复杂度:O(n!)O(n!)O(n!) 跟全排列差不多

但是上面的代码要怎么优化呢?

注意:所有随着递归会变化的东西都需要放在函数的参数里面。

map[i][j] //i到j距离
bool vis[n] = {false};
int search(int idx,bool vis[]){
    if(vis中true的数量为n){
        return 0;
    }
    if(dp[idx][vis]算过){ //怎么存这个状态呢?用Map<Pair<int,bool[]>>
        return dp[idx][vis];
    }
    int min = 1e8;
    for(int i=0;i<n;i++){
        if(!vis[i]){
            vis[i] = true;
            int t = search(i,vis) + map[idx][i];
            if(t<min){
                min = t;
            }
            vis[i] = false; //复原,假装你没有访问过i(因为下一步i++,访问的就是i+1了)
        }
    }
    return min;
}
  1. 原来时间复杂度为O(n!)O(n!)O(n!)(暴力搜索)
  2. 增加了O(n∗2n)O(n*2^n)O(n2n)的空间后,新的时间复杂度为O(n2∗2n)O(n^2*2^n)O(n22n)
    可以看出确实减少了冗余。

Tips

状态压缩:把数组压缩为一个整数

bool vis[4] = {false,true,true,false};
可以用int类型数组来存:
int vis[4] = {0,1,1,0};
再想想,还可以用字符串来存储呀!
string s = "0110";
最后!这不就是一连串二进制数么:
int res = (0110)2 = 6(10) 转换为10进制就OK啦!

也可以节省空间啦!原来sizeof(vis[4])可能等于4,16。。。
但是转换成int类型就是4个字节啦!

总结

  • 滚动数组,状态压缩,升维,单调性,四边形不等式(高级套路)
  • 本质:先暴力,找冗余,去冗余
动态规划是一种算法设计技术,通常用于解决最优化问题,它通过将大问题分解成相互重叠的小子问题,并存储每个子问题的解,避免了必要的重复计算,从而提高了解题效率。以下是关于动态规划一个1000字左右的笔记概要: **一、动态规划的基本思想** 1. **自底向上(Bottom-Up)**: 从最小规模的问题开始递推,逐层构建解决方案,最终得到最大规模问题的最优解。 2. **最优子结构(Optimal Substructure)**: 问题的最优解可以由其子问题的最优解组合而成。 **二、基本步骤** 1. **划分状态空间**: 确定问题的状态变量和状态转移方程。 2. **定义边界条件**: 定义基础情况,即最小规模问题的直接解。 3. **填充表格**: 根据状态转移方程,逐步填充表单,存储已计算结果。 4. **回溯求解**: 最终状态的结果就是原问题的解答。 **三、经典应用举例** 1. **斐波那契数列**: F(n) = F(n-1) + F(n-2)动态规划能高效地找出第n项。 2. **背包问题**: 求解物品价值最大化的问题,考虑物品选择的同组合。 **、注意事项** 1. 避免重复计算:利用数组或二维矩阵记录已计算结果。 2. 子问题间的关系:确保子问题是独立的,而是有依赖关系。 3. 更新策略:选择合适的数据结构和更新规则。 **五、复杂度分析** - 时间复杂度:取决于问题的大小和子问题的重复程度。理论上,如果子问题数量为n,则时间复杂度可能是O(n^2)、O(n^3)等。 - 空间复杂度:需要存储中间结果,空间复杂度通常是O(n)或O(n^2)。 **六、实际案例分析** 详细讲解几个实际问题如何运用动态规划解决,例如最长公共子序列、最短路径等。 **七、总结及实践建议** - 动态规划适用于具有重叠子问题和最优子结构的问题。 - 通过实例理解并掌握分治法和动态规划的区别。 - 练习编写伪代码,熟悉动态规划的思维过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值