題目
題意:判斷非負整數 n (n<=1 000 000)能否表示爲各不相同的非負數字的階乘之和,比如9 = 1!+2!+3!
解題思路
由於這些非負數字可以不是相鄰的,所以要枚舉所有的組合,那麼就可以用二進制取子集的方法。
利用二進制的“開關”特性枚舉;
具體爲:假設給定集合A大小爲n,則想象A = {a[0], a[1], …, a[n-1]}的每個元素對應一個開關位(0或1),0表示不出現,1表示出現;當每個元素的開關位的值確定時,就得到一個子集,因此共有2^n-1種情況(全0爲空集);
我們利用區間[1, 2^n-1]上的每個整數來對應每個子集,對應方法是:遍歷該整數二進制表示的每一位,若該位爲1則相應子集中存在對應元素,否則不存在。
在本題中,由於
遍歷
需要注意的是:
- 提前打表可以節約後續的計算時間,即將所有滿足題意的數先標記出來,判斷時直接查詢即可。
- 子集法中的0(空集)在本題屬於特殊情況,因爲0不能由其它數階乘得到,但在枚舉時中會被標記。
AC代碼
#include <iostream>
using namespace std;
const int maxn = 1e7 << 1;
bool judge[maxn]; //打表判斷i是否爲階乘之和
int fac[15]; //存放階乘
void init()
{
//計算階乘
fac[0] = 1;
for (int i = 1; i <= 10; ++i)
fac[i] = fac[i-1] * i;
//打表
fill(judge, judge+maxn, false);
int sum = 0;
for (int i = 0; i < (1<<10); ++i) //二進制子集法,遍歷2^10-1個子集
{
sum = 0;
for (int j = 0; j < 10; ++j) //移位檢測1出現的位置
if (i & (1<<j))
sum += fac[j];
judge[sum] = true;
}
judge[0] = false; //特殊:0不是階乘之和!
}
int main()
{
int n, m;
init();
while (cin >> n && n >= 0)
{
if (judge[n]) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}