给定一个非负整数 nn,请你判断是否存在一些整数 xixi,能够使得 n=∑1≤i≤txi!n=∑1≤i≤txi!,其中 t≥1,xi≥0,xi=xj iff i=jt≥1,xi≥0,xi=xj iff i=j。
iffiff 表示当且仅当。
输入格式
输入包含多组测试数据。
每组数据占一行,包含一个非负整数 nn。
最后一行是一个负数,表示输入结束,无需处理。
输出格式
每组数据输出一行结果,如果 nn 能表示为若干数的阶乘之和,则输出 YES
,否则输出 NO
。
数据范围
0≤n≤1060≤n≤106,
每组输入最多包含 100100 组数据。
输入样例:
9
-1
输出样例:
YES
这个题目值得说的是二进制枚举
我们就是利用了二进制的特性 ,我们就可以用0和1代表选和不选。
如果选出为1 2 3 4 5的位置,则如下表:
1 2 3 4 5
二进制 1 0 1 1 0
状态 选 不选 选 选 不选
上面说有5个位置如果全选的话就是11111对应的十进制数就是31, 这个区间上每一个整数代表一个集合一共 32个集合,上表就是数字22(二进制:10110)所代表的集合:1 3 4。
所以我们遍历每一个集合:
for(int i = 0; i < (1 << n); i++)
设s = 13(二进制为1101)代表我们选0 2 3位置上的数值;
那么我们如何找到每个位置上的数值呢?
我们遍历的是二进制的十进制表示(比如13),我们当然可以转化为二进制再枚举每一位,但是,这很麻烦;
一个很巧妙的方式就是利用位运算。
1<<0=1(0);
1<<1=2(10);
1<<2=4(100);
1<<3=8(1000);
1<<4=16(10000);
...
1<<7=128(10000000);
...
看出来了吧!我们只需要将13&(1<<i)我们便可以得到每一位是不是1 (1<< i 除了那一位,剩余的都是0,所以我们就可以得到那一位是不是1)
-------------------------------------------------------------------------------------------------------分割线----------------------------------------------------------------------------------
现在讲本题,2的10次方就超过了10^6了,所以枚举
1到10的和是不是存在,就用到了二进制枚举了
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
int f[10];
set<int> S;
int main()
{
for (int i = 0; i < 10; i ++ )
{
f[i] = 1;
for (int j = i; j; j -- )
f[i] *= j;
}
for (int i = 1; i < 1<<10; i ++ )
{
int s = 0;
for (int j = 0; j < 10; j ++ )
if (i&(1<<j))
s += f[j];
S.insert(s);
}
int n;
while (cin >> n, n >= 0)
if (S.count(n))
puts("YES");
else
puts("NO");
return 0;
}