zzuli1430 多少个0 (dp递推

本文探讨了一个动态规划解决方案,用于计算在特定矩阵中从左上角到右下角的路径,使得乘积后的末尾0数量最少。通过状态转移和剪枝优化,实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1430: 多少个0

时间限制: 1 Sec   内存限制: 128 MB
提交: 181   解决: 37

题目描述

一个n*n的方格,每个格子中间有一个数字是2或者5,现在从方格的左上角走到右下角,每次只能选择向下或者向右移动一格两种移动方式,让所有经过的格子中的数字相乘,求使最后的结果中末尾处0的数字最少。

输入

第一行是一个正整数n(0<n<100)

接下来n行是一个n*n的矩阵。

输出

一个正整数m,表示最后的结果末尾处最少有x0

样例输入

4
2 5 2 5
5 2 5 2
2 5 5 5
2 2 2 2

样例输出

1

解题思路:

本题的状态转移不同于普通的递推,因为要求的是相乘后末尾0的数量最少,用搜索剪枝的方法看似可解,但因刚说的原因,在实现的过程中会有很多麻烦,很容易超时,所以还是考虑动态规划解法。

首先,由乘法结合律,末尾0的数量可由序列中2和5配对的数量得出,有一对2,5相乘,即有一个末尾0.


接下来这里有一个关键点,即:按题目要求从左上走到右下,所经过的格子总数一定是S=2×n-1个!

这一点很关键,想到这一点之后本题基本就可以写dp了,可惜不是自己想到的,多亏学弟的提醒

这样一来,2和5的总数就知道了,那么只要分别统计2和5能达到的最大数量(暂设为M2和M5),即可算出对与这两种情况最后分别会有多少末尾0

例如:

以样例数据为例,n=4,S=2*n-1=7

M2=6,M5=4

对于M2,该情况下有6个2,即有1个5,末尾0数即为1;

对于M5,该情况下有4个5,即有3个2,末尾0数即为3;

两相比较,得出最终答案应为1.


Ps:注意不能只统计2或5,例如对样例数据,如果只统计5的话,就无法得出最优解了



代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

int map[105][105],dp2[105][105],dp5[105][105];

int main()
{
	int n;
	while(cin>>n)
	{
		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				scanf("%d",&map[i][j]);
			}
		}
		memset(dp2,0,sizeof(dp2));
		memset(dp5,0,sizeof(dp5));

		for(int i=1;i<=n;++i)
		{
			for(int j=1;j<=n;++j)
			{
				dp2[i][j]=max(dp2[i-1][j],dp2[i][j-1]);
				if(map[i][j]==2)	++dp2[i][j];

				dp5[i][j]=max(dp5[i-1][j],dp5[i][j-1]);
				if(map[i][j]==5)	++dp5[i][j];
			}
		}

		int ans=max(dp2[n][n],dp5[n][n]);
		if(ans*2>(2*n-1))	ans=2*n-1-ans;
		cout<<ans<<endl;
	}
	return 0;
}


### ZZULI OJ 1178 单词数解决方案 #### 解决方案概述 ZZULI OJ 1178 是关于统计给定字符串中不同单词数量的问题。程序需要处理多组测试数据,直到输入 `#` 结束。对于每一行输入,计算并输出该行中的不同单词数目。 #### 输入与输出说明 - **输入**: 多行字符序列,每行为一组测试数据,最后一行由单独的 `#` 终止。 - **输出**: 对于每一个测试案例,在新行打印不同的单词总数。 #### 实现逻辑分析 为了实现上述功能,采用如下方法: - 使用双重循环结构来解析每个单词,并将其存入临时数组中[^2]。 - 利用辅助函数 `strcmp()` 来比较两个字符串是否相等,从而判断当前读取的单词之前是否已经存在过。 - 如果发现重复,则跳过计数;反之则增加唯一单词的数量。 - 当遇到空格或其他分隔符时认为是一个完整的单词结束标志位[^3]。 #### 完整代码示例 以下是基于 C 语言编写的完整解答版本: ```c #include <stdio.h> #include <string.h> int main() { char arr[] = "#", str[100010]; while (gets(str), strcmp(str, arr) != 0) { char p[1010][33]; // 存储单个测试样例内的所有单词 int i, j = 0, k = 0; for (i = 0; i < strlen(str); ) { if (str[i] == ' ') { ++i; } else { while ((str[i] != ' ') && (str[i] != '\0')) p[j][k++] = str[i++]; p[j][k] = '\0'; ++j; k = 0; } } int sum = 0; for (int i = 0; i < j; ++i) { int flag = 1; for (int m = 0; m < i; ++m) { if (!strcmp(p[m], p[i])) { flag = 0; break; } } if (flag) ++sum; } printf("%d\n", sum); } return 0; } ``` 此段代码实现了对多个字符串样本的不同单词计数的功能,并按照题目要求进行了相应的优化和改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值