今天完成了自己的第一道洛谷绿题、蓝题,成就感满满,继续努力!!!
DFS剪枝的简单讲解
剪枝讲解: 剪枝是在dfs的基础上减少搜索次数,有问题可见上一篇博客
- 目的: 减少搜索次数
- 方式: 对于一些对结果不会再次产生影响的路径,不再继续搜索
- 可行性剪枝:不行的就不搜
- 最优性剪枝:不如现在结果好的就不搜
- 记忆化搜索:将结果进行保存,需要用时取出,不再重复搜索
- 待学习
- 搜索顺序剪枝
- 排除等效冗余
习题冲冲冲!
数的划分—洛谷
-
题目
-
代码
#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; }
- 题解
该题所用剪枝方式为可行性剪枝,代码第13行限制了i的上限,原因我们举例说明:后续还剩6个数,我们现在距离目标值n还剩12,那么剩下的值就最多选到12 / 6 = 2。超过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; }
- 题解
该题所用剪枝方式为可行性剪枝、最优性剪枝,第17行为可行性剪枝,第18行为最优性剪枝。使用最优性剪枝时,你要对比当前状态与已存储答案,当前状态若不如已存储答案,那么继续搜索也一定不会使其好转,无需搜索。 对于其他,我对24行与33 - 34行代码进行解读:- 24行:继续搜索,向上一层,对应r,h向上给,s,v进行更新。
- 33行:sk、vk为当前层至第1层的最小面积与最小体积(蛋糕顶部为第一层);
最长距离—洛谷
-
题目
-
代码
#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; }
- 题解
该题所用剪枝方式为可行性剪枝、记忆化搜索- 可行性剪枝的使用在于,对于到达当前位置所需移除箱子数大于题目给出的移箱能力的话,无需继续搜索
- 记忆化搜索的使用在于,对于我们之前到过的位置,如果所需步数更多,那么我们能从该点到达的位置一定不如之前多,无需继续搜索。其实也是一种最优性搜索。对于记忆化搜索,一定要对之前的一些信息进行记录
- 对于其他部分,我稍作讲解:
- 38 - 39为每一次搜索前的初始化