2024.2.5学习笔记(dfs剪枝)

本文介绍了在洛谷编程题中如何运用DFS(深度优先搜索)的剪枝技巧,包括可行性剪枝(如限制搜索范围)和最优性剪枝(如记忆化搜索,避免重复计算),以减少搜索次数,优化算法效率。通过实例分析了数的划分、生日蛋糕和最长距离问题的解决方案。
摘要由CSDN通过智能技术生成

今天完成了自己的第一道洛谷绿题、蓝题,成就感满满,继续努力!!!

DFS剪枝的简单讲解

剪枝讲解: 剪枝是在dfs的基础上减少搜索次数,有问题可见上一篇博客

  1. 目的: 减少搜索次数
  2. 方式: 对于一些对结果不会再次产生影响的路径,不再继续搜索
    1. 可行性剪枝:不行的就不搜
    2. 最优性剪枝:不如现在结果好的就不搜
    3. 记忆化搜索:将结果进行保存,需要用时取出,不再重复搜索
    4. 待学习
      1. 搜索顺序剪枝
      2. 排除等效冗余

习题冲冲冲!

数的划分—洛谷

  1. 题目

    [NOIP2001 提高组] 数的划分 - 洛谷

  2. 代码

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,k,ans;
    
    //x为上一个数,sum是当前和,cnt为当前数的个数
    void dfs(int x,int sum,int cnt){
      if(cnt == k){
        if(sum == n) ans++;
        return;
      }
      
      for(int i = x;i <= (n - sum) / (k - cnt);i++){
        dfs(i,sum + i,cnt + 1);
      }
    }
    
    int main(){
      cin>>n>>k;
      dfs(1,0,0);
      cout<<ans<<endl;
      return 0;
    }
    
    
  3. 题解

    该题所用剪枝方式为可行性剪枝,代码第13行限制了i的上限,原因我们举例说明:后续还剩6个数,我们现在距离目标值n还剩12,那么剩下的值就最多选到12 / 6 = 2。超过2的部分一定不是正确答案,故而不需要进行搜索,这就是可行性剪枝的思想。

    其他内容比较好理解,就不再进行讲解


生日蛋糕—洛谷

  1. 题目

    [NOI1999] 生日蛋糕 - 洛谷

  2. 代码

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m;
    int sk[20],vk[20];//sk是从1层到当前层的最小面积,vk是……体积
    const int INF = 0x3f3f3f3f;
    int ans = 0x3f3f3f3f;
    
    //蛋糕层数,上一层半径,上一层高度,
    //蛋糕当前面积,蛋糕当前体积
    void dfs(int k,int r,int h,int s,int v){
      int MAX_h = h;
      if(k == 0){
        if(v == n) ans = min(s,ans);//条件,v 与 s都对应,则保存答案
        return;
      }
      if(v + vk[k - 1] > n) return;//可行性剪枝,vk为上层蛋糕最小体积,与当前体积相加大于n,必然不行
      if(s + 2 * (n - v) / r >= ans) return;//最优性剪枝,对于表面积大于当前答案的,无需继续搜索。
      
      for(int i = r - 1;i >= k;i--){//
        if(k == m) s = i * i;
        MAX_h = min(h - 1,(n - vk[k - 1] - v) / i / i);
        for(int j = MAX_h;j >= k;j--){
          dfs(k - 1,i,j,s + 2 * i * j,v + i * i * j);
        }
      }
      
    }
    
    int main(){
      cin>>n>>m;
      for(int i = 1;i <= m;i++){
        sk[i] = sk[i - 1] + 2 * i * i;
        vk[i] = vk[i - 1] + i * i * i;
      }
      dfs(m,n,n,0,0);
      
      if(ans == INF) cout<<0;
      else cout<<ans;
      return 0;
    }
    
    
  3. 题解
    该题所用剪枝方式为可行性剪枝、最优性剪枝,第17行为可行性剪枝,第18行为最优性剪枝。使用最优性剪枝时,你要对比当前状态与已存储答案,当前状态若不如已存储答案,那么继续搜索也一定不会使其好转,无需搜索。 对于其他,我对24行与33 - 34行代码进行解读:
    1. 24行:继续搜索,向上一层,对应r,h向上给,s,v进行更新。
    2. 33行:sk、vk为当前层至第1层的最小面积与最小体积(蛋糕顶部为第一层);

最长距离—洛谷

  1. 题目

    [SCOI2009] 最长距离 - 洛谷

  2. 代码

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m,t;
    int a[40][40];
    int dis[40][40];//dis为起点到当前位置的方块数
    int vis[40][40];//vis[x][y] = 1代表已走过,= 0代表未走过
    
    int d[4][4] = {{0,1},{0,-1},{1,0},{-1,0}};
    
    
    void dfs(int x,int y,int cnt){
      if(cnt > t) return;//可行性剪枝
      if(dis[x][y] <= cnt) return;//记忆化搜索
      dis[x][y] = cnt;
      for(int i = 0;i < 4;i++){
        int nx = x + d[i][0],ny = y + d[i][1];
        if(nx == 0 || ny == 0 || nx == n + 1 || ny == m + 1) continue;
        vis[nx][ny] = 1;
        dfs(nx,ny,cnt + a[nx][ny]);
        vis[nx][ny] = 0;
      }
    }
    
    
    int main(){
      cin>>n>>m>>t;
      for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
          char c; cin>>c;
          a[i][j] = c - '0';
        }
      }
      
      int ans = 0;
      for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
          memset(dis,0x3f,sizeof(dis));
          memset(vis,0,sizeof(vis));
          int cnt = a[i][j];//cnt记录当前已挪出的障碍数
          dfs(i,j,cnt);
          for(int x = 1;x <= n;x++){
            for(int y = 1;y <= m;y++){
              if(dis[x][y] <= t)
                ans = max(ans,(x - i) * (x - i) + (y - j) * (y - j));
            }
          }
        }
      }
      
      cout<<fixed<<setprecision(6)<<sqrt(ans);
      
      return 0;
    }
  3. 题解
    该题所用剪枝方式为可行性剪枝、记忆化搜索
    1. 可行性剪枝的使用在于,对于到达当前位置所需移除箱子数大于题目给出的移箱能力的话,无需继续搜索
    2. 记忆化搜索的使用在于,对于我们之前到过的位置,如果所需步数更多,那么我们能从该点到达的位置一定不如之前多,无需继续搜索。其实也是一种最优性搜索。对于记忆化搜索,一定要对之前的一些信息进行记录
    3. 对于其他部分,我稍作讲解:
      1. 38 - 39为每一次搜索前的初始化
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值