0%

3382.整数拆分

3382. 整数拆分

题目描述:

一个整数总可以拆分为 $ 2 $ 的幂的和。

例如: $ 7 $ 可以拆分成

共计 $ 6 $ 种不同拆分方式。

再比如: $ 4 $ 可以拆分成:

用 $ f(n) $ 表示 $ n $ 的不同拆分的种数,例如 $ f(7)=6 $ 。

要求编写程序,读入 $ n $ ,输出 $ f(n)\mod10^9 $ 。

输入格式

一个整数 $ n $ 。

输出格式

一个整数,表示 $ f(n)\mod10^9 $ 。

数据范围

$ 1 \le N \le 10^6 $

输入样例:

1
7

输出样例:

1
6

题解:

完全背包做法:

可以把 $ n $ 看做背包容量,那么有 $ \log_2(n) $ 个物品 $ 1, 2, 4, 8, \cdots $ ,每个物品都是无限的,就是个完全背包,不过完全背包是求最大值的。

发现是从上一行的该列和上一行的前几列转移过来的。可以使用一维数组优化

因为无限取,需要顺序枚举。

该题目是求方案数,可以对方程修改为相加即可。

从前往后枚举

递推做法:

可以发现,如果是奇数的话,和前一个偶数是相等的。

如果是偶数的话,

  • 偶数首先可以分为 $ 1 $ 加一个奇数,方案数是等于奇数拆分的方案数,这是包含 $ 1 $ 的拆分。
  • 不包含 $ 1 $ 的拆分,最小的拆分值为 $ 2 $ ,不可能会出现奇数。因此刚好等于奇数 $ \frac{n}{2} $ 的方案数。

代码:

完全背包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using namespace std;
using namespace FAST_IO;
const ll mod = 1e9;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-5;
const int maxn = 1e6 + 10;
const int maxm = 1e5 + 10;
int t, n, m, k;
ll dp[maxn];
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
dp[0] = 1;
cin >> n;
for (int i = 1; i <= n; i *= 2)
{
for (int j = i; j <= n; j++)
{
dp[j] = (dp[j] + dp[j - i]) % mod;
}
}
cout << dp[n] << endl;
return 0;
}

直接递推

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using namespace std;
using namespace FAST_IO;
const ll mod = 1e9;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-5;
const int maxn = 1e6 + 10;
const int maxm = 1e5 + 10;
int t, n, m, k;
ll dp[maxn];
int main()
{
// #define COMP_DATA
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
dp[0] = 1;
cin >> n;
for(int i = 1; i<= n; i++)
{
dp[i] = (dp[i - 1] + (!(i & 1)) * dp[i / 2]) % mod; // 直接规范化一下,一个式子解决
}
cout << dp[n] << endl;
return 0;
}