1. 题目来源
链接:3481. 阶乘的和
2. 题目解析
阶乘增长速度很快,9!=362880,0!=1
,所以其实就是前 10 个数只能选 1 次,能否凑出 n
的问题。
也就是枚举这 10 个数的子集。
二进制枚举,dfs
,01 背包均可。
暴力 dfs 代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10;
int n;
int a[N];
// 这里其实是 u 位置之前的选取情况,并不包含 u 本身是否被选取
bool dfs(int u, int n) {
if (n < 0 || (n > 0 && u > 9)) return false;
// if (n < 0 || u > 10) return false; 数组越界也不报错...
if (n == 0) return true;
if (dfs(u + 1, n - a[u])) return true;
if (dfs(u + 1, n)) return true;
return false;
}
int main() {
a[0] = 1;
for (int i = 1; i < 10; i ++ ) a[i] = a[i - 1] * i; // a[9] = 362880
while (scanf("%d", &n), n >= 0) {
if (dfs(0, n) && n) printf("YES\n");
else printf("NO\n");
}
return 0;
}
dfs+记录,来自群友的错误代码,大家一起找错。
注释那段是 dfs
的错误代码,因为 pos==10
的时候,其实是从 pos==9, dfs(pos+1, sum + a[pos])
传过来的,这个时候,若 a[9]
被选中,那么就被直接 return
掉了,并没有进行插入,所以导致最后一个数 a[9]
,永远是不可选中状态。
故应该先插入,再判断位置!而我昨天是直接先写了 if (n < 0 || u > 9) return false;
导致一直出错。
应该是要先判断 if (n==0) return true;
,再写 if (n < 0 || u > 9) return false;
如果顺序颠倒则造成了本来 n==0
但是被错误判断掉了这种情况。所以我在里面加了 if (n < 0 || (n > 0 && u > 9)) return false;
这个条件,但其实还是理解的不够深刻!
这位群友和我昨天犯了一样的错误,故印象深刻特此记录。
#include <iostream>
#include <set>
using namespace std;
const int N = 15;
int n;
int a[N];
set<int> s;
void DFS(int pos, int sum) {
// if (sum > 1e6 || pos == 10) return ;
if(sum > 1e6) return ;
s.insert(sum);
if (pos == 10) return ;
DFS(pos + 1,sum);
DFS(pos + 1,sum + a[pos]);
}
int main() {
a[0] = 1;
for(int i = 1; i <= 9; i++) a[i] = a[i - 1] * i;
DFS(0,0);
while(cin >> n,n >= 0)
{
if(!n)
{
cout << "NO" << endl;
continue;
}
if(s.count(n)) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
二进制枚举:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>
using namespace std;
const int N = 10;
int n;
int a[N];
unordered_set<int> S;
int main() {
a[0] = 1;
for (int i = 1; i < 10; i ++ ) a[i] = a[i - 1] * i;
for (int i = 1; i < 1 << 10; i ++ ) { // 不能一个数都不选,没意义。且一个数都不选为 0,造成错误情况 0!=1
int t = 0;
for (int j = 0; j < 10; j ++ ) {
if (i >> j & 1)
t += a[j];
}
S.insert(t);
}
while (scanf("%d", &n), n >= 0) {
if (S.count(n)) puts("YES");
else puts("NO");
}
return 0;
}
01 背包,来自 mrk
大佬 %%%:
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e6 + 5;
typedef long long LL;
int n;
bool f[N];
int main() {
f[0] = 1;
int s = 1;
for (int i = 0; ; i++) {
for (int j = 1000000; j >= s; j--) f[j] |= f[j - s];
if ((LL)s * (i + 1) > 1e6) break;
s *= (i + 1);
}
while (cin >> n) {
if (n < 0) return 0;
if (n == 0) puts("NO");
else puts(f[n] ? "YES" : "NO");
}
}