【编程题】
【题目描述】 :东东在一本古籍上看到有一种神奇数,如果能够将一个数的数字分成两组,其中一组数字的和等于另一组数字的和,我们就将这个数称为神奇数。例如242就是一个神奇数,我们能够将这个数的数字分成两组,分别是{2,2}以及{4},而且这两组数的和都是4.东东现在需要统计给定区间中有多少个神奇数,即给定区间[l, r],统计这个区间中有多少个神奇数,请你来帮助他。
输入描述
输入包括一行,一行中两个整数l和r ( 1 ≤ l , r ≤ 1 0 9 , 0 ≤ r − l ≤ 1 0 6 ) (1 ≤ l, r ≤ 10^9, 0 ≤ r - l ≤ 10^6) (1≤l,r≤109,0≤r−l≤106),以空格分割
*输出描述
输出一个整数,即区间内的神奇数个数
输入示例
1 50
输出示例
4
解题思路:
首先这道题和Leetcode上的416题Partition Equal Subset Sum的解法是一样的。 这是一道动态规划中的01背包问题。
解题步骤:
- 要把数字按位转化成数组。
- 每位数字累计求和
- 数组中元素和是否能累加成半和。(01背包问题)
C/C++版
#include <iostream>
using namespace std;
bool isMagicArr(int index, int sum, int* nums)
{
if (sum == 0) // 递归结束条件1
return true;
if (sum < 0 || index < 0) // 递归条件结束2, 累加和超过了或者数组中没有值了
return false;
// num[index]是sum中的一部分或者不是sum中的一部分
return isMagicArr(index - 1, sum - nums[index], nums) || isMagicArr(index - 1, sum, nums);
}
bool checkMagic(int num)
{
int nums[11]; // 把数字num按位存到nums中
int index = 0;
int sum = 0; // 每位数字求和
for ( ; num > 0; index++)
{
nums[index] = num % 10; // 获取并保存每位数字
sum += nums[index]; // 每位数字累计求和
num = num / 10; // 除去个位数字
}
// 每位数和是奇数,则肯定不是神奇数
if (sum % 2 != 0)
return false;
return isMagicArr(index, sum / 2, nums);
}
int main()
{
int l = 0, r = 0;
while (true)
{
cin >> l >> r;
int count = 0;
if (l > r)
break;
for (int num = l; num <= r; num++)
{
if (checkMagic(num))
count++;
}
cout << count << endl;
}
system("pause");
return 0;
}
java版
import java.util.Scanner;
/**
* Author: snowy
* 京东2018秋招笔试题2: 神奇数 同leetcode上第416题
* 【题目描述】 链接:https://www.nowcoder.com/questionTerminal/56d818ae68134c12b26e81f41ecafb9e
* 东东在一本古籍上看到有一种神奇数,如果能够将一个数的数字分成两组,其中一组数字的和等于另一组数字的和,我们就将这个数称为神奇数。
* 例如242就是一个神奇数,我们能够将这个数的数字分成两组,分别是{2,2}以及{4},而且这两组数的和都是4.
* 东东现在需要统计给定区间中有多少个神奇数,即给定区间[l, r],统计这个区间中有多少个神奇数,请你来帮助他。
*
* 输入描述: 输入包括一行,一行中两个整数l和r(1 ≤ l, r ≤ 10^9, 0 ≤ r - l ≤ 10^6),以空格分割
* 输出描述: 输出一个整数,即区间内的神奇数个数
* */
public class CheckMagic {
// 不带有数组优化的
public static boolean checkMagic(int n) {
int []nums = new int[11]; // 把数字n按位存到nums中
int index = 0;
int sum = 0; // 每位数字求和
for (; n > 0; index ++) {
nums[index] = n % 10; // 获取并保存每位数字
sum += nums[index]; // 每位数字累计求和
n /= 10; // 除去个位数字
}
if (sum % 2 != 0)
return false;
return isMagic(index, sum/2, nums);
}
public static boolean isMagic(int index, int sum, int[] nums) {
if(sum == 0)
return true;
if(index < 0 || sum < 0)
return false;
return isMagic(index - 1, sum - nums[index], nums) || isMagic(index - 1, sum, nums);
}
// 带有滚动数组的优化
public static boolean checkMagicWithRollarray(int n) {
int []nums = new int[11]; // 把数字n按位存到nums中
int index = 0;
int sum = 0; // 每位数字求和
for (; n > 0; index ++) {
nums[index] = n % 10; // 获取并保存每位数字
sum += nums[index]; // 每位数字累计求和
n /= 10; // 除去个位数字
}
if (sum % 2 != 0)
return false;
boolean []dp = new boolean[sum / 2 + 1];
dp[0] = true;
return isMagicWithRoll(index, sum/2, nums, dp);
}
private static boolean isMagicWithRoll(int index, int sum, int[] nums, boolean[] dp) {
for (int i = 0; i < index; i ++) { // 遍历nums数组
for (int s = sum; s >= nums[i]; s --) {
if (s - nums[i] == 0)
dp[nums[i]] = true;
else
dp[s] = dp[s] || dp[s - nums[i]];
}
}
return dp[sum];
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Pleace input:");
while (input.hasNext()) {
int l = input.nextInt();
int r = input.nextInt();
if ( l > r)
break;
int count = 0;
for (int num = l; num <= r; num ++) {
if(checkMagicWithRollarray(num))
count ++;
}
System.out.println(count);
System.out.println("Pleace input:");
}
}
}
思考:在这道题目中数字数要按照为进行拆分的,因此1055这个数字就不是神奇数,如果不局限于按照位进行拆分,则该数字能拆分成{10}和{5,5},这样是否也是可行的,对于这种情况下的神奇数有空的时候补上相应的程序。