最近在参加哆嗒数学网组织的闯关活动,第4关的题目为:
用0到9组成一个无重复位的数字,要求结果不能是2、3或者5的倍数,即因子中不能含有2、3、5中的任何一个.
求出满足条件的最大数字.
今天闲来无事,用程序解决了这个问题.
基本思路为,首先考虑十个数码全用的情况,此时各位的和为45,则此时无论怎么排列,结果都能被3整除,不符合题意.
所以程序的任务是从这n=10个digit中删掉尽可能少的数字,使总和不能被3整除,然后把这些数位进行排列,把得到的结果从大到小排列,然后找出其中第一个满足因子不含2或5这个条件的数字即可.具体代码为
#include<cassert>
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
using namespace std;
struct Combination {
const int m; //解的位数
vector<int> answer; //目前的答案,是一个长度为m的升序序列
const int max_value; //可能的最大值
Combination(const vector<int>& values, int m)
: m(m)
, answer(m)
, max_value(values.back())
{
//把s中的前m个元素复制到answer中,作为初始解
for (int i = 0; i < m; ++i) {
int x = values[i];
answer[i] = values[i];
}
}
void print()const {
for (auto e : answer)
cout << e << " ";
cout << endl;
}
//把第index的值+1,右边各位等于左边的+1
void inc(int index) {
int x = answer[index];
for (int i = index; i < m; ++i)
answer[i] = ++x;
assert(answer.back() <= max_value);
}
bool next() //输出下一个组合,如果已经到头,则返回false
{
/*从右边开始找到第一个可以加1的位,即本位+1之后,这个值到最大值的距离
>=右边的位数
*/
for (int i = m - 1, j = 0; i >= 0; --i, ++j) {
int x = answer[i];
if (max_value - x > j) //本位+1之后,右边的j位仍然有可能组成一个升序序列
{
/*新的子序列为
x+1,x+2,x+(j+1)<=max
*/
//从本位开始构造一个连续的升序子序列
inc(i);
return true;
}
}
return false;
}
};
//把一个数字组成值
int64_t get_value(const vector<int>& v) {
int64_t p = 1;
int64_t sum = 0;
for (size_t i = 0; i < v.size(); ++i, p *= 10) {
sum += (v[i] * p);
}
return sum;
}
int main() {
vector<int> values(10);
set<int> s;
for (int i = 0; i < 10; ++i) {
values[i] = i;
s.insert(i);
}
for (int m = 1; m <= 9; ++m) {
//从这10个数中扣掉m个数
Combination c1(values, m);
set<int64_t> answers;
do {
//计算扣掉以后的和
int sum = 45;
set<int> s1 = s;
for (auto e : c1.answer) {
sum -= e;
s1.erase(e);
}
if (sum % 3 != 0) {
//一个数能被3整除的条件是各位之和能被3整除
//不能被3整除时,遍历n-m个数的所有排列
vector<int> v;
for (auto e : s1)
v.push_back(e);
//按升序遍历当前组合的所有可能排列
do {
int x = v[0];
if (x % 2 && x % 5) //判断当前解是否合理
{
int64_t y = get_value(v);
answers.insert(y);
/*注意此时得到的
不一定是全局最优解,只是当前数码组合下的最大解.
*/
break; //
}
} while (next_permutation(v.begin(), v.end()));
}
} while (c1.next());
if (!answers.empty()) {
cout << (*(answers.rbegin())) << endl;
break;
}
}
return 0;
}
输出结果为
987654301
容易验证它对3的余数为1,符合要求