探寻宝藏 【算法设计与分析课设】 c语言代码 + 思路详解 + 三维优化

1、题目

传说HMH大沙漠中有一个M*N迷宫,里面藏有许多宝物。某天,Dr.Kong找到了迷宫的地图,他发现迷宫内处处有宝物,最珍贵的宝物就藏在右下角,迷宫的进出口在左上角。当然,迷宫中的通路不是平坦的,到处都是陷阱。Dr.Kong决定让他的机器人卡多去探险。

但机器人卡多从左上角走到右下角时,只会向下走或者向右走。从右下角往回走到左上角时,只会向上走或者向左走,而且卡多不走回头路。(即:一个点最多经过一次)。当然卡多顺手也拿走沿路的每个宝物。

Dr.Kong希望他的机器人卡多尽量多地带出宝物。请你编写程序,帮助Dr.Kong计算一下,卡多最多能带出多少宝物。

输入

第一行: K 表示有多少组测试数据。

接下来对每组测试数据:

第1行: M N

第2~M+1行:Ai1 Ai2 ……AiN (i=1,…..,m)

【约束条件】

2≤k≤5 1≤M, N≤50 0≤Aij≤100 (i=1,….,M; j=1,…,N)

所有数据都是整数。 数据之间有一个空格。

输出

对于每组测试数据,输出一行:机器人卡多携带出最多价值的宝物数

样例输入

2
2 3
0 10 10
10 10 80
3 3
0 3 9
2 8 5
5 7 100

样例输出

120
134

2、思路1

假设出发点为A终点为B ,题目要求从A->B,然后再从B->A,求出这两条路径上取得的最大的和(路径不能重复)。我们可以假设同时从A出发,得到两条不同路径,这是相同的效果,相当于从A->B一共走了两次。

我们首先考虑只走一次。

只走一次:

状态表示: f[i,j]表示所有从(1,1)走到(i,j)的路径之和的最大值

状态转移:

由于限制了只会向下走或者向右走,因此到达(i,j)有两条路径

  • 从上方转移过来,f[i][j] = f[i][j-1]+ a[i][j]
  • 从左方转移过来,f[i][j] = f[i-1][i]+ a[i][j]

因此,状态计算方程为: f[i][j] = max(f[i - 1][j], f[i][j - 1]) + a[i][j], 从向右和向左两条路径中选择路径之和最大的转移过来,再加上a[i][j]的值。

现在我们考虑走两次。

走两次:

状态表示: f[i1,j1,i2,j2]表示所有从(1,1),(1,1)分别走到(i1,j1),(i2,j2)的路径之和的最大值。

状态计算:

(1,1),(1,1)分别走到(i1,j1)(i2,j2)的前一步共有四条路径:

  • 第一条:下 第二条:下

    f[i1][j1][i2][j2] = f[i1-1]][j1][i2-1][j2] + a[i1][j1] + a[i2][j2]

  • 第一条:下 第二条:右

    f[i1][j1][i2][j2] = f[i1-1][j1][i2][j2-1] + a[i1][j1] + a[i2][j2]

  • 第一条:右 第二条:下

    f[i1][j1][i2][j2] = f[i1][j1-1][i2-1][j2] + a[i1][j1] + a[i2][j2]

  • 第一条:右 第二条:右

    f[i1][j1][i2][j2] = f[i1][j1-1][i2][j2-1] + a[i1][j1] + a[i2][j2]

因此,状态计算方程为: f[i1][j1][i2][j2] = max(f[i1][j1][i2][j2],f[i1-1][j1][i2][j2-1],f[i1][j1-1][i2-1][j2],f[i1][j1-1][i2][j2-1]) + a[i1][j1] + a[i2][j2]

注意点:

因为一个点只能走一次,包括终点也是如此。因此,我们考虑终点的前一步,最后再加上终点的值。而终点的前一步共有两种可能,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eIANr5JC-1624705440780)(算法课设.assets/image-20210625211954443.png)]

故最后的答案取值为:max(f[n][m-1][n-1][m],f[n-1][m][n][m-1]) + a[n][m]

时间复杂度分析:4重循环,因此时间复杂度为 O ( n ∗ n ∗ m ∗ m ) O(n*n*m*m) O(nnmm)

空间复杂度分析: 定义状态为4维,因此空间复杂度为 O ( n ∗ n ∗ m ∗ m ) O(n*n*m*m) O(nnmm)

完整分析图示:
在这里插入图片描述

3、代码1

#include<stdio.h>
#include<string.h>

const int N = 55;
int a[N][N];
int f[N][N][N][N]; 

int max(int a,int b)
{
	return a>b?a:b;
} 

int main()
{
	int k,n,m;
	scanf("%d",&k);
	while(k--)
	{
		scanf("%d%d",&n,&m);
		memset(f,0,sizeof(f));
		memset(a,0,sizeof(a));
		for(int i = 1; i <= n; i++)
		   for(int j = 1; j <= m; j++)
		     scanf("%d",&a[i][j]);
		for(int i1 = 1; i1 <= n; i1++)
		    for(int j1 = 1; j1 <= m; j1++)
				for(int i2 = 1; i2 <= n; i2++)
					for(int j2 = 1; j2 <= m; j2++)
					{
						if(i1 == i2 && j1 == j2)  continue;  //走到同一格子
						int &x = f[i1][j1][i2][j2];
						int t = a[i1][j1] + a[i2][j2]; 
						x = max(x,f[i1-1][j1][i2-1][j2]+t);
						x = max(x,f[i1-1][j1][i2][j2-1]+t);
						x = max(x,f[i1][j1-1][i2-1][j2]+t);
						x = max(x,f[i1][j1-1][i2][j2-1]+t); 					
					}
		printf("%d\n",max(f[n][m-1][n-1][m],f[n-1][m][n][m-1]) + a[n][m]);	  
	}
	return 0;
} 

4、思路2

第一种思路的时间复杂度为 O ( n ∗ n ∗ m ∗ m ) O(n*n*m*m) O(nnmm) ,当nm很大时,这样的效率显然我们是不能接受的。有没有一种方法可以降低时间复杂度那?答案是有的。

这道题的关键在于如何处理” 同一个格子只能走一次 “?

我们发现只有在i1 + j1 == i2 +j2时,两条路径的格子才可能重合 。我们让 k = i1 + j1 == i2 +j2,两个人同时从A出发,每个人走的步数和是一样的。

由此我们就可以把路径长度k,作为动态规划的阶段,每个阶段中,我们同时把两条路径扩展一步,即路径长度加 1,来进入下一个阶段,而路径长度加1后,无非就是向下走一格或是向右走一格,对应横纵坐标的变换。

新的状态表示:f[k, i1, i2]表示所有从(1,1),(1,1)分别走到(i1,k-i1), (i2,k-i2)的路径之和的最大值,k表示两条路线当前走到的格子的横纵坐标之和(或者说两个人都走了k步)。

k = i1 + j1 = i2+ j2

因此把状态由f[i1][j1][i2][j2]优化成三维f[k][i1][i2]等价于f[i1][k−i1][i2]k−i2]

状态计算:

(1,1),(1,1)分别走到(i1,j1)(i2,j2)的前一步共有四条路径:

  • 第一条:下 第二条:下

    f[i1-1]][j1][i2-1][j2] == f[k-1][i1-1][i2-1];

  • 第一条:下 第二条:右

    f[i1-1][j1][i2][j2-1] == f[k-1][i1-1][i2];

  • 第一条:右 第二条:下

    f[i1][j1-1][i2-1][j2] == f[k-1][i1][i2-1];

  • 第一条:右 第二条:右

    f[i1][j1-1][i2][j2-1] == f[k-1][i1][i2];

我们解释上面的一种状态:

f[i1-1]][j1][i2-1][j2] == f[k-1][i1-1][i2-1]:代表两个人都走了k-1步,A从(i1-1,j1)走到(i1,j1),B从(i2-1,j2)走到(i2,j2)

因此,状态计算方程为: f[k][i1][j1] = max(f[k-1][i1-1][i2-1],f[k-1][i1-1][i2],f[k-1][i1][i2-1],f[k-1][i1][i2]) + a[i1][j1] + a[i2][j2]

注意点: 同思路1一样

最终的答案为: max(f[n+m-1][n][n-1],f[n+m-1][n-1][n])+a[n][m]

时间复杂度分析:3重循环,因此时间复杂度为 O ( ( n + m ) ∗ n ∗ n ) O((n+m)*n*n) O((n+m)nn)

空间复杂度分析: 定义状态为3维,因此空间复杂度为 O ( ( n + m ) ∗ n ∗ n ) O((n+m)*n*n) O((n+m)nn)

完整分析图示:
在这里插入图片描述

5、代码2

#include<stdio.h>
#include<string.h>

const int N = 55;
int a[N][N];
int f[2*N][N][N]; 

int max(int a,int b)
{
	return a>b?a:b;
} 

int main()
{
	int k,n,m;
	scanf("%d",&k);
	while(k--)
	{
		scanf("%d%d",&n,&m);
		memset(f,0,sizeof(f));
		memset(a,0,sizeof(a));
		for(int i = 1; i <= n; i++)
		   for(int j = 1; j <= m; j++)
		     scanf("%d",&a[i][j]);
		for(int k = 2; k <= n+m; k++)
		    for(int i1 = 1; i1 <= n; i1++)
				for(int i2 = 1; i2 <= n; i2++)				
				{
				 	int j1=k-i1,j2=k-i2;
				 	if(i1 >= 1 && j1 <= n && i2 >=1 && j2 <=m)
				 	{
					    if(i1 == i2) continue;
						int &x=f[k][i1][i2];
						int t = a[i1][j1] + a[i2][j2]; 
						x=max(x,f[k-1][i1-1][i2-1]+t);
                		x=max(x,f[k-1][i1-1][i2]+t);
               			x=max(x,f[k-1][i1][i2-1]+t);
                		x=max(x,f[k-1][i1][i2]+t);
					} 					
				}
		printf("%d\n",max(f[n+m-1][n][n-1],f[n+m-1][n-1][n])+a[n][m]);	  
	}
	return 0;
} 

在这里插入图片描述

算法设计与分析》目录: 第一篇引入篇 第1章算法概述1.1用计算机求解问题与算法 1.1.1用计算机求解问题的步骤 1.1.2算法及其要素和特性 1.1.3算法设计及基本方法 1.1.4从算法到实现 1.2算法描述 1.2.1算法描述简介 1.2.2算法描述约定 1.2.3一个简单问题的求解过程 1.3现代常用算法概览* 1.3.1压缩算法 1.3.2加密算法 1.3.3人工智能算法 1.3.4并行算法 1.3.5其他实用算法 第2章算法分析基础 2.1算法分析体系及计量 2.1.1算法分析的评价体系 2.1.2算法的时间复杂性 2.1.3算法的空间复杂性 2.1.4NP完全性问题 2.2算法分析实例 2.2.1非递归算法分析 2.2.2递归算法分析 2.2.3提高算法质量 第二篇基础篇 第3章算法基本工具和优化技巧3.1循环与递归 3.1.1循环设计要点 3.1.2递归设计要点 3.1.3循环与递归的比较 3.2算法与数据结构 3.2.1原始信息与处理结果的对应存储 3.2.2数组使信息有序化 3.2.3数组记录状态信息 3.2.4大整数存储及运算 3.2.5构造趣味矩阵 3.3优化算法的基本技巧 3.3.1算术运算的妙用 3.3.2标志量的妙用 3.3.3信息数字化 3.4优化算法的数学模型 3.4.1杨辉三角形的应用 3.4.2最大公约数的应用 3.4.3公倍数的应用 3.4.4斐波那契数列的应用 3.4.5递推关系求解方程 习题 第三篇核心篇 第4章基本的算法策略4.1迭代算法 4.1.1递推法 4.1.2倒推法 4.1.3迭代法解方程 4.2蛮力法 4.2.1枚举法 4.2.2其他范例 4.3分治算法 4.3.1分治算法框架 4.3.2二分法 4.3.3二分法变异 4.3.4其他分治方法 4.4贪婪算法 4.4.1可绝对贪婪问题 4.4.2相对或近似贪婪问题 4.4.3贪婪策略算法设计框架 4.5动态规划 4.5.1认识动态规划 4.5.2动态规划算法设计框架 4.5.3突出阶段性的动态规划应用 4.5.4突出递推的动态规划应用 4.6算法策略间的比较 4.6.1不同算法策略特点小结 4.6.2算法策略间的关联 4.6.3算法策略侧重的问题类型 习题 第5章图的搜索算法 5.1图搜索概述 5.1.1图及其术语 5.1.2图搜索及其术语 5.2广度优先搜索 5.2.1算法框架 5.2.2广度优先搜索的应用 5.3深度优先搜索 5.3.1算法框架 5.3.2深度优先搜索的应用 5.4回溯法 5.4.1认识回溯法 5.4.2回溯法算法框架 5.4.3应用1——基本的回溯搜索 5.4.4应用2——排列及排列树的回溯搜索 5.4.5应用3——最优化问题的回溯搜索 5.5分支限界法 5.5.1分支搜索算法 5.5.2分支限界搜索算法 5.5.3算法框架 5.6 图的搜索算法小结 习题 第四篇应用篇 第6章算法设计实践6.1循环赛日程表 6.2求3个数的最小公倍数 6.3猴子选大王 6.4最大子段和问题 6.5背包问题 6.5.1与利润无关的背包问题 6.5.2与利润有关的背包问题
评论 46
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林小鹿@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值