东东在一本古籍上看到有一种神奇数,如果能够将一个数的数字分成两组,其中一组数字的和等于另外一组数字的和,我们就将这个数称为神奇数。例如242就是神奇数,我们能够将这个数字分成两组,分别是{2,2}以及{4},给定区间[l,r],统计这个区间有多少个神奇数,请你来帮助他。
首先判断数组能否被平分,即数组分割问题,
dp[i][j]
表示数组前
i
个数字能否求和得到
则
dp[i][j]=dp[i−1][j]||dp[i−1][j−array[i]]
其中||是逻辑或运算。
优化:
1、若sum(array)为奇数,直接返回false
2、使用逆序循环将dp数组简化为一维数组
[l,r]区间很大,可以使用map保存已经计算过神奇数的数,之后如果有元素一致的数,可以直接查询结果。
如计算到”12345”为非神奇数,则之后遍历到”21345”、”23145”、”23415”、”23451”、“31245”、、、、都是非神奇数
#include <iostream>
#include <math.h>
#include <vector>
#include <algorithm>
#include <numeric>
#include <map>
using namespace::std;
//#define debug_
int lef = 0, righ = 0;
bool IsMagical(vector<char>& vec)
{
int len = vec.size();
int sum = accumulate(vec.begin(), vec.end(), 0);
if (sum & 1)
return false;
int mid = (sum>>1);
vector<int> dp(mid + 1, 0);
dp[0] = 1;
for (int i = 0; i < len; ++i)
{
for (int j = mid; j > 0; --j)
{
if (j >= vec[i])
dp[j] = max(dp[j], dp[j - vec[i]]);
}
}
if (dp[mid])
return true;
else
return false;
}
vector<char> getsortnum(int i)
{
vector<char> vec;
vec.reserve(10);
int tmp;
while (i)
{
tmp = i % 10;
i = i / 10;
vec.push_back(tmp);
}
sort(vec.begin(), vec.end());
return vec;
}
void func(int lef, int righ)
{
int count(0);
bool flag;
map<vector<char>, bool> my_map;
for (auto i = lef; i <= righ; ++i)
{
vector<char> sorted_num;
sorted_num.reserve(10);
char tmp(0);
int i_tmp(i);
while (i_tmp)
{
tmp = i_tmp % 10;
i_tmp = i_tmp / 10;
sorted_num.push_back(tmp);
}
sort(sorted_num.begin(), sorted_num.end());
auto iter = my_map.find(sorted_num);
if (iter == my_map.end())
{
flag = IsMagical(sorted_num);
my_map[sorted_num] = flag;
if (flag)
++count;
}
else
{
if (iter->second)
++count;
}
}
cout<< count<<endl;
}
int main()
{
#ifdef debug_
lef = 1;
righ = 50;
#else
cin >> lef;
cin >> righ;
#endif
func(lef, righ);
return 0;
}