取数字问题

取数字问题

题目

给定M*N的矩阵,其中的每个元素都是-10到10之间的整数。你的任务是从左上角(1,1)走到右下角(M,N),每一步只能向右或向下,并且不能走出矩阵的范围。你所经过的方格里面的数字都必须被选取,请找出一条最合适的道路,使得在路上被选取的数字之和是尽可能小的正整数。

输入

第一行两个整数M,N,(2<=M,N<=10),分别表示矩阵的行和列的数目。
接下来的M行,每行包括N个整数,就是矩阵中的每一行的N个元素。

输入样例

2 2
0 2
1 0

输出

仅一行一个整数,表示所选道路上数字之和所能达到的最小的正整数。如果不能达到任何正整数就输出-1。

输出样例

1

思路

利用数组保存所有的状态,可用记忆化搜索或DP来写。
其实本题有超多种方法,但是本蒟蒻只会两种(递归和递推):

方法一(递推)

先枚举一个结果,然后从(n,m)dfs到(1,1),使当前值为上一个存结果的减去当前的本来的数值,当(1,1)为0时,就是可以从(n,m)到(1,1),否则枚举下一个a,因为有负数所以存的时候要加一个M。

方法二(递归)

用一个数组f[i][j][k]来表示第i行第j列是否能得到数字k,但k是已经加了一个M的,所以在输出时要从M+1开始。
动态转移方程为:
{ i f ( f [ i − 1 ] [ j ] [ k ] ) f [ i ] [ j ] [ k + a [ i ] [ j ] ] = 1 i f ( f [ i ] [ j − 1 ] [ k ] ) f [ i ] [ j ] [ k + a [ i ] [ j ] ] = 1 \left\{ \begin{array}{rcl} if(f[i−1][j][k]) f[i][j][k+a[i][j]]=1\\ if(f[i][j−1][k]) f[i][j][k+a[i][j]]=1\\ \end{array} \right. {if(f[i1][j][k])f[i][j][k+a[i][j]]=1if(f[i][j1][k])f[i][j][k+a[i][j]]=1
tips: 第一行的为取上的数,第二行的为取左的数

代码

方法一
#include<cstdio>
using namespace std;
const int M=1001;//定义M为边界,让后面打得方便一点
int f[11][11][2001],a[11][11],n,m,ans=-1;//初始化
void hhd(int x,int y,int d)//递归过程
{
	f[x][y][d+M]=1;//标记走过的
	if (f[1][1][M]) return;//判断是否到终点
	if (x>1&&!f[x-1][y][d-a[x-1][y]+M]) hhd(x-1,y,d-a[x-1][y]);//枚举上面的数
	if (y>1&&!f[x][y-1][d-a[x][y-1]+M]) hhd(x,y-1,d-a[x][y-1]);//枚举左边的数
}
int main()
{
	scanf("%d%d",&n,&m);//读入
	for (int i=1;i<=n;i++)
	for (int j=1;j<=m;j++)
	scanf("%d",&a[i][j]);//读入
	for (int i=1;i<=n*m*10;i++)//从小找到大
	{
		hhd(n,m,i-a[n][m]);//递归
		if (f[1][1][M])//如果成立
		{
			printf("%d",i);//输出
			return 0;//直接退出
		}
	}
	printf("-1");//如果都不可以则输出-1
	return 0;
}
方法二
#include<cstdio>
#include<iostream>
using namespace std;
const int M=1000;//定义M,让后面打得方便一点
int n,m,a[11][11],temp;//初始化
bool  f[11][11][2001];//初始化
int main()
{
	scanf("%d%d",&n,&m);//读入
	for (int i=1;i<=n;i++)
	for (int j=1;j<=m;j++)
	scanf("%d",&a[i][j]);//读入
	temp=a[1][1]+M;//后面要用
	f[1][1][temp]=1;//标记第一个点
	for (int i=2;i<=n;i++) {f[i][1][temp+a[i][1]]=1;temp+=a[i][1];}//预先处理第一列
	temp=a[1][1]+M;//后面要用
	for (int i=2;i<=m;i++) {f[1][i][temp+a[1][i]]=1;temp+=a[1][i];}//预先处理第一行
	for (int i=2;i<=n;i++)//枚举行
	for (int j=2;j<=m;j++)//枚举列
	for (int k=1;k<=2*M;k++)//枚举值
	{
		 if (f[i][j-1][k]) f[i][j][k+a[i][j]]=1;//动态转移方程
		 if (f[i-1][j][k]) f[i][j][k+a[i][j]]=1;//动态转移方程
	}
	for (int k=M+1;k<=M*2;k++)//从小到大枚举每一个在范围内的正整数
	if (f[n][m][k]) {printf("%d",k-M);return 0;}//如果是就输出
	printf("-1");//否则输出-1
	return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Matlab中,要有效数字可以使用自定义函数或者使用内置函数。引用\[1\]中提供了一个自定义函数的示例,该函数可以保留指定位数的有效数字。你可以将需要有效数字的数值作为参数传递给这个函数,然后指定要保留的有效数字位数。这个函数会返回保留指定位数有效数字后的结果。 另外,引用\[2\]中提到,Matlab默认显示小数点后四位小数,但实际上计算精度并没有受到影响。如果你想查看更多位数的小数,可以使用"format long"命令来改变显示格式。 此外,引用\[3\]中提到了一种方法,可以通过尝试不同的E值来获所需的有效数字。具体来说,可以尝试将E设置为1,并逐渐增加E,直到得到的结果不再是0。然后,舍弃最后的E位数,即可得到所需的有效数字结果。 综上所述,你可以使用自定义函数、内置函数或者尝试不同的E值来得Matlab中的有效数字。 #### 引用[.reference_title] - *1* [matlab保留n位有效数字](https://blog.csdn.net/m0_61738169/article/details/127704404)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [【matlab】 关于保留小数位数&有效数字问题](https://blog.csdn.net/u013085981/article/details/45192189)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [matlab – 使用变精度算术获得数字的第一个有效数字](https://blog.csdn.net/weixin_39827304/article/details/115831346)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值