基本概念的理解:https://www.zhoulujun.cn/html/theory/engineering/model/7307.html
数字金字塔
题目描述:
(请分别使用分治、记忆化搜索和递推三种方法完成)
观察下面的数字金字塔。写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以从当前点走到左下方的点也可以到达右下方的点。
在上面的样例中,从13到8到26到15到24的路径产生了最大的和86。
输入:
第一个行包含R(1<= R<=1000),表示行的数目。后面每行为这个数字金字塔特定行包含的整数。所有的被供应的整数是非负的且不大于100。
输出:
单独的一行,包含那个可能得到的最大的和。
样例输入:
5 //数塔层数
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11
样例输出:
86
深搜(Depth First Search)
深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法;
利用DFS算法求解出每条路径数字累加和;用一个变量记录所有可能的值中最大值,即为问题的解。
#include <iostream>
using namespace std;
// 定义数字金字塔数组,ans
int data[105][105], ans=0, n;
/*
@parm:x、y 表示数字的在金字塔中的位置,初始值位1,1表示第一个元素
*/
void dfs(int x, int y, int curr){
if(x==n){
ans = ans < curr ? curr : ans;
return ;
} // 每次在递归出口处算出最大值
dfs(x+1,y,curr+data[x+1][y]);
dfs(x+1,y+1,curr+data[x+1][y+1]);
}
int main(){
memset(data, 0, sizeof(data));
cin >> n;
for (int i=1; i<=n; i++) {
for(int j=1; j<=i; j++) {
cin >> data[i][j];
}
} // 数字金字塔赋初值
dfs(1,1,data[1][1]);
cout << ans << endl;
return 0;
}
分治法求解
分治:将原问题分解为若干个规模较小但类似于原问题的子问题(Divide),递归
地求解这些子问题(Conquer),然后再合并这些子问题的解来建立原问题的解(Merge)。
对于这种写法,其函数含义为:用当前位置值data[x][y])
加上正下方与右下方数字和较大值max(dfs(x+1,y),dfs(x+1,y+1))
,由于一开始下方的较大数字和
是未知的,所以一直递归到边界(金字塔最后一层),再递归回去即可求出最大路径数字和;
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
/*
data: 存放数字金字塔
n:金字塔的层数
*/
int data[105][105], n;
/*
@param x、y : 表示元素在金字塔中的位置;初始值为1,1表示第一个元素
*/
int dfs(int x, int y){
if(x==n){
return data[x][y];
}
return data[x][y] + max(dfs(x+1, y), dfs( x+1, y+1)); //当前数 + 下一层到n-1层的最大数字和
}
int main(){
cin >> n;
for (int i=1; i<=n; i++) {
for(int j=1; j<=i; j++) {
cin >> data[i][j];
}
} // 数字金字塔赋初值
cout << dfs(1,1) << endl;
return 0;
}
记忆化搜索
可以看出"分治法"求解过程存在很多重复计算,很容易想到用记忆化搜索来避免重复
代码如下:
#include <iostream>
#include <cmath>
using namespace std;
//备忘录算法求解数字金字塔
int data[105][105]; //存放数字金字塔
int n; //金字塔的层数
int p[105][105]; //备忘录
/*
@param x、y : 表示元素在金字塔中的位置;初始值为1,1表示第一个元素
*/
int dfs(int x, int y){
if(p[x][y] !=-1){
return p[x][y]; //如果备忘录里面有,直接返回
}
if(x==n){
return data[x][y];
}
p[x][y] = data[x][y] + max(dfs(x+1, y), dfs( x+1, y+1));
return p[x][y]; //当前数 + 下一层到n-1层的最大数字和
}
int main(){
memset(p, -1, sizeof(p)); //初始化备忘录
cin >> n;
for (int i=1; i<=n; i++) {
for(int j=1; j<=i; j++) {
cin >> data[i][j];
}
} // 数组赋值
cout << dfs(1,1) << endl;
return 0;
}
递推
自下而上,从已知到未知;
#include <iostream>
#include <cmath>
#define MAX 105
using namespace std;
// 递推求解数字金字塔
/*
5
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11
*/
int data[MAX][MAX];
int main(){
int n; //5
cin >> n;
for(int i=1; i<=n; i++){
for(int j=1; j<=i; j++){
cin >> data[i][j];
}
}
for(int i=n-1; i>=1; i--){
for(int j=1; j<=i;j++){
data[i][j] = max(data[i+1][j],data[i+1][j+1]) + data[i][j]; // 自底向上
}
}
cout << data[1][1] <<endl;
}
递推参考资料:
https://blog.csdn.net/weixin_45592404/article/details/104319004
bfs实现图形面积计算
题目描述:
编程计算由“"号围成的下列图形的面积。面积计算方法是统计号所围成的闭合曲线中水平线和垂直线交点的数目。如下图所示,在10*10的二维数组中,有"*"围住了15个点,因此面积为15。输入如下:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 * * * 0 0 0
0 0 0 0 * 0 0 * 0 0
0 0 0 0 0 * 0 0 * 0
0 0 * 0 0 0 * 0 * 0
0 * 0 * 0 * 0 0 * 0
0 * 0 0 * * 0 * * 0
0 0 * 0 0 0 0 * 0 0
0 0 0 * * * * * 0 0
0 0 0 0 0 0 0 0 0 0
思路:用bfs得到一个bool数组,它记录入过队列的’0’(这些元素是被访问过的),而被"*"围起来的0是未被访问的;根据这个特点产生如下判别条件:如果数组元素是’0’并且这个元素没有被访问过,该元素即为面积的一部分
代码实现:
#include <iostream>
#include <queue>
using namespace std;
struct node{
int x,y; // 位置(x,y)
} Node;
// const int maxn = 20;
int n = 10; //图形size
char matrix[10][10];
bool inq[10][10]={false}; //记录位置(x,y)是否已入过队
int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; // 增量数组
// 判断坐标(x,y)是否需要被访问,需要返回true,否则返回false
bool judge(int x, int y){
// 越界返回flase
if(x>=n || x<0 || y>=n || y<0 ) return false;
// 如果(x,y)已入过队列,或(当前位置为'*'),返回false
if(inq[x][y] || matrix[x][y]=='*') return false;
//以上都不满足返回true
return true;
}
void bfs(int x, int y){
queue<node> Q; //定义队列
Node.x = x, Node.y = y; // 当前节点的坐标为(x,y)
Q.push(Node); // 将节点Node入队
inq[x][y] = true;
while(!Q.empty()){
node top = Q.front(); //取出队首元素
Q.pop(); // 队首元素出队
// 循环4次,得到4个相邻位置
for(int i=0; i<4; i++){
int newX = top.x + dir[i][0];
int newY = top.y + dir[i][1];
// 如果新位置(newX,newY)需要访问
if(judge(newX, newY)){
Node.x = newX;
Node.y = newY;
Q.push(Node); // 将节点加入队列
inq[newX][newY] = true; // 设置位置(newX,newY)已经入过队列
}
}
}
}
int main(){
for (int i=0; i<n; i++){
for (int j=0; j<n; j++){
cin >> matrix[i][j]; //初始化图形,char型数组录入的是字符'0'
}
}
int count = 0; // 面积
bfs(0,0);
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
// 如果元素为0且没有访问过则说明是被围起来的面积
if(matrix[i][j] == '0' && !inq[i][j]){
count++;
}
}
}
cout << count << endl;
return 0;
}
bfs学习参考资料:
- https://www.bilibili.com/video/BV1AE411a7Wp?from=search&seid=16783551617652526193
- 《算法笔记》
总结
通过本次实验,
- 对分治、记忆化搜索、递推有了一定了解;
- 学习了广度优先搜索,C++STL中queue的使用;