3034. 数字拆分

wang2018

证明过程
【转】 原文链接:https://blog.csdn.net/zhang20072844/java/article/details/17033931
————————————————

记f(n)为n的划分数,我们有递推公式:
f(2m + 1) = f(2m),
f(2m) = f(2m - 1) + f(m),
初始条件:f(1) = 1。

证明:

证明的要点是考虑划分中是否有1。

记:
A(n) = n的所有划分组成的集合,
B(n) = n的所有含有1的划分组成的集合,
C(n) = n的所有不含1的划分组成的集合,
则有: A(n) = B(n)∪C(n)。

又记:
f(n) = A(n)中元素的个数,
g(n) = B(n)中元素的个数,
h(n) = C(n)中元素的个数,
易知: f(n) = g(n) + h(n)。

以上记号的具体例子见文末。

我们先来证明: f(2m + 1) = f(2m),
首先,2m + 1 的每个划分中至少有一个1,去掉这个1,就得到 2m 的一个划分,故 f(2m + 1)≤f(2m)。
其次,2m 的每个划分加上个1,就构成了 2m + 1 的一个划分,故 f(2m)≤f(2m + 1)。
综上,f(2m + 1) = f(2m)。

接着我们要证明: f(2m) = f(2m - 1) + f(m),
把 B(2m) 中的划分中的1去掉一个,就得到 A(2m - 1) 中的一个划分,故 g(2m)≤f(2m - 1)。
把 A(2m - 1) 中的划分加上一个1,就得到 B(2m) 中的一个划分,故 f(2m - 1)≤g(2m)。
综上,g(2m) = f(2m - 1)。

把 C(2m) 中的划分的元素都除以2,就得到 A(m) 中的一个划分,故 h(2m)≤f(m)。
把 A(m) 中的划分的元素都乘2,就得到 C(2m) 中的一个划分,故 f(m)≤h(2m)。
综上,h(2m) = f(m)。

所以: f(2m) = g(2m) + h(2m) = f(2m - 1) + f(m)。                                            

这就证明了我们的递推公式。

记 f(0) = 1,根据递推公式,可以得到:
f(2m) = f(0) + f(1) + … + f(m)。
一些例子:

A(3) = {
  (1,1,1)
  (1,2)
},
f(3) = 2,

A(4) = {
 (1,1,1,1)
  (1,1,2)
  (2,2)
  (4)
},
f(4) = 4,

A(5) = {
 (1,1,1,1,1)
  (1,1,1,2)
  (1,2,2)
  (1,4)
},
f(5) = 4,

A(6) = {
 (1,1,1,1,1,1)
  (1,1,1,1,2)
  (1,1,2,2)
  (1,1,4)
  (2,2,2)
  (2,4)
},
f(6) = 6,

B(6) = {
 (1,1,1,1,1,1)
  (1,1,1,1,2)
  (1,1,2,2)
  (1,1,4)
},
g(6) = 4,

C(6) = {
  (2,2,2)
  (2,4)
},
h(6) = 2.

10175102262 LarsPendragon

(捂脸)自己动态规划还是个小白……自己想了半天写出来的算法还是垃圾,最后看了别人的算法恍然大悟。
对于f(n),有两种情况,第一是n为奇数,这时f(n)=f(n-1),第二种是n为偶数,这时f(n)可以表示为f(n-1)的所有表示后再加1,以及f(n/2)的所有表示乘2。
附C代码:

#include <stdio.h>
int main()
{
    long long ans[MAX]={0};
    int i, j, n;
    ans[0]=1;
    ans[1]=1;
    for(i=2; i<1000001; i++)
    {
        if(i%2) ans[i]=ans[i-1];
        else ans[i]=(ans[i-1]+ans[i/2])%1000000000;
    }
    scanf("%d",&n);
    for(i=0; i<n; i++) 
    {
        scanf("%d",&j);
        printf("case #%d:\n%lld\n",i,ans[j]);
    }
    return 0;
}
Kevin_K

Hints:EOJ嘛~大家都懂的,暴力完全背包毫无压力。

cd106224
//状态:分类讨论
//偶数:包含1 与不包含1
//奇数:与前一个dp相等
#include <iostream>
#include <algorithm>
using namespace std;
using LL = long long;
const int mod = 1000000000;
const int maxn = 1000001;
LL dp[maxn];

int main() {
    int n;
    int num;
    cin >> num;
    for (int k = 0; k < num; ++k) {
        cin >> n;
        fill(dp, dp + maxn, 0);
        dp[1] = 1;
        for (int i = 2; i <= n; ++i) {
            if (i & 1) {
                dp[i] = dp[i - 1];
            }
            else {
                dp[i] = (dp[i - 1] + dp[i >> 1]) % mod;
            }
        }
        cout << "case #" << k << ":" << endl;
        cout << dp[n] % mod << endl;
    }
    return 0;
}
Fifnmar

这题太鬼畜了,怎么还要对 1‘000‘000‘000 取模都不告诉我们……还是看评论区的大佬才知道的。

用完全背包妥妥就过了,复杂度是 $O(n\log n)$ 对于百万级的 $n$ 完全够用。

#include "bits/stdc++.h"
using namespace std;
using u64 = uint64_t;

int main() {
    u64 t;
    cin >> t;
    for (u64 query = 0; query < t; ++query) {
        cout << "case #" << query << ":\n";
        u64 n;
        cin >> n;
        vector<u64> dp(n + 1, 0);
        dp[0] = 1;
        for (u64 i = 1; i <= n; i <<= 1) {
            for (u64 j = i; j <= n; ++j) {
                constexpr static u64 kMod = 1'000'000'000;
                dp[j] = (dp[j - i] + dp[j]) % kMod;
            }
        }
        cout << dp[n] << '\n';
    }
}
MayDick

题目有说对10亿取模?

Kevin_K

有的,查看网页源代码了解一下.
%前没有\就炸了,以前应该是能显示的…
加上以后大概这样:

请编写程序,读入一个正整数 n (1≤n≤1000000),输出 f(n)\%1000000000。

Kevin_K

大概170行左右吧.

你当前正在回复 博客/题目
存在问题!