【题解】整数划分 [51nod1201] 整数划分 V2 [51nod1259]
【题解】整数划分 [51nod1201] 整数划分 V2 [51nod1259]
传送门:整数划分 \([51nod1201]\) 整数划分 \(V2\) \([51nod1259]\)**
【题目描述】
\(【T1】\)
将整数 \(N\) 划分为若干个不同整数的和,有多少种不同的划分方式,答案对 \(10^9 + 7\) 取模。
例:\(n=6\),\(n\) 可划分为 \(\{6\} \{1,5\} \{2,4\} \{1,2,3\}\) 共 \(4\) 种。
【样例】
样例输入:
6
样例输出:
4
【数据范围】
\(100\%\) \(1 \leqslant N \leqslant 50000\)
\(【T2】\)
将 \(N\) 划分为若干个整数的和,有多少种不同的划分方式,答案对 \(10^9 + 7\) 取模。
例:\(n=4\),\(n\) 可划分为 \(\{4\} \{1,3\} \{2,2\} \{1,1,2\} \{1,1,1,1\}\) 共 \(5\) 种。
【样例】
样例输入:
4
样例输出:
5
【数据范围】
\(100\%\) \(1 \leqslant N \leqslant 50000\)
【分析】
【球盒问题】
这两道题实际上是一种球盒问题的变型。
球盒问题见隔壁 【学习笔记】薛定谔的喵咪 \(Cat\) \(—\) 球盒问题(全详解)。
先来看 \(T2\),想象一下,有 \(n\) 个 \(1\) 整整齐齐地站成一排,你可以将其划分为 \(m\) 个区间,其中 \(m \in [1,n]\)。实际上就是要把 \(n\) 个相同的球放进 \(m\) 个相同的盒子,盒子不可为空。
可以发现,如果随意放的话,会有大量的重复情况。而每一种分配方式都可以将盒子按照球的数量从大到小排序,排出来一个非严格降序(或升序)的序列。所以,每次为新的盒子分配时,只要分配的球数小于等于上一次的分配球数,就可以保证不重不漏。
设 \(dp[i][j]\) 表示将 \(j\) 个球放入 \(i\) 个盒子的方案数。
\(dp\) 方程为: \(dp[i][j]=dp[i−1][j−1]+dp[i][j−i]\) 。
用简单一些的方式来理解(我也不知道是否严谨,数学证明见隔壁):
\(dp[i-1][j-1]\) 指的是新的盒子 \(i\) 里面只放了一个球。
\(dp[i][j-i]\) 指的是在所有已经分好的 \(i\) 个盒子里面都多放一个球。
那么 \(T1\) 呢?由于同一整数只能用一次,即任意两个盒子里面不能放入相同数量的球,那么降序排列后就应该是严格单调下降的,新盒子所分配的球数需要小于上一次的分配球数。
多了一些限制,还是一样的状态表示,\(dp[i][j-i]\) 方程中的仍可保留。
而 \(dp[i-1][j-1]\) 出现了一个问题:每次都在新盒子中放 \(1\) 会触犯到限制。但如果先把前面 \(i-1\) 个盒子都加 \(1\),新盒子就可以放 \(1\) 了,所以改为 \(dp[i-1][j-(i-1)-1]\) 。
\(dp\) 方程为:\(dp[i][j]=dp[i−1][j−i]+dp[i][j−i]\) 。
【背包问题】
用背包也可以做做。
将 \(1,2,3,4...n\) 使作 \(n\) 个不同的物品,其编号就是体积。
用 \(dp[j]\) 表示选了总体积为 \(V\) 的物品的方案数,正序枚举 \(i\) 的同时保证了物品的选择保持单调不下降态,至于后面的 \(j\),两道题用不同的做法。
\(T1\) 倒序枚举,即 \(01\) 背包。\(T2\) 正序枚举,即完全背包。
\(dp\) 方程均为:\(dp[j]+=dp[j-i]\) 。
但以上两种做法时间复杂度均为是 \(O(n^2)\),需要想办法优化。
【优化加速】
【T1】
考虑【球盒问题】的优化。
如果尽量让每个盒子中的球数最小,那么选出来的盒子那么选出来的升序序列就是这样子的:\(1,2,3...n\),而使用的盒子数量最大值就是此时的 \(m\)。
序列之和 \(\frac {m(m+1)}{2}=n\),\(m\) 算出来大概是 \(\sqrt{2n}+1\),所以枚举盒子数时只需要枚举 \(1\) 到 \(m\) 就可以了。
时间复杂度为 \(O(n\sqrt{2n})\)
【T2】
由于可以重复分配某一数量的球,盒子数最大可以达到 \(n\) (全部都只放一个),\(T1\) 的优化不再适用。
考虑分成 \([1,m-1],[m,n]\) 两块计算。
即分别算出只使用某块以内的数来组成 \(n\) 的方案数,然后乘法原理再求和即可。
前面部分就用【完全背包】,不多废话。
后面部分用【球盒问题】解决。
由于可以选的数大于等于 \(m\),所用盒子数量必定小于等于 \(\lceil \frac{n}{m}\rceil\),因此盒子数量只需要枚举 \([1,\lceil \frac{n}{m}\rceil]\)。
只是这次可选的最小数变成了 \(m\) 而不是 \(1\),所以将 \(dp[i-1][j-1]\) 改成 \(dp[i-1][j-m]\),即在新的盒子 \(i\) 里面放 \(m\) 个球。
\(dp\) 方程为:\(dp[i][j]=dp[i−1][j−m]+dp[i][j−i]\) 。
总时间复杂度为 \(O(n(m+\frac{n}{m}))\)。
由均值不等式可知:\(m+\frac{n}{m} \geqslant 2\sqrt{m*\frac{n}{m}} = 2\sqrt{n}\),当且仅当 \(m=\frac{n}{m}\) 时等号成立,所以 \(m\) 取 \(\sqrt{n}+1\) 即可(老师说每次使用 \(sqrt\) 时都要注意精度误差,在后面加个 \(1\) 比较保险)。
【Code】
【T1】
#include<algorithm>
#include<cstdio>
#include<cmath>
#define Re register int
const int N=5e4+3,P=1e9+7;
int n,m,ans,dp[320][N];
int main(){
scanf("%d",&n);m=sqrt(2*n)+1;
dp[0][0]=1;
for(Re i=1;i<=m;++i)
for(Re j=i;j<=n;++j)
(dp[i][j]=(dp[i-1][j-i]+dp[i][j-i])%P)%=P;
for(Re i=1;i<=m;++i)(ans+=dp[i][n])%=P;
printf("%d",ans);
}
【T2】
#include<algorithm>
#include<cstdio>
#include<cmath>
#define Re register int
const int N=5e4+3,P=1e9+7;
int n,m,ans,f2[250][N],ans1[N],ans2[N];
int main(){
scanf("%d",&n);m=sqrt(n)+1;
ans1[0]=1;
for(Re i=1;i<m;++i)
for(Re j=i;j<=n;++j)
(ans1[j]+=ans1[j-i])%=P;
f2[0][0]=ans2[0]=1;
for(Re i=1;i<=m;++i)
for(Re j=m;j<=n;++j){//j>=i&&j>=m,又因为i<=m所以j从m开始枚举
(f2[i][j]+=(f2[i-1][j-m]+f2[i][j-i])%P)%=P;
(ans2[j]+=f2[i][j])%=P;
}
for(Re i=0;i<=n;++i)(ans+=(long long)ans1[i]*ans2[n-i]%P)%=P;
printf("%d",ans);
}
【题解】整数划分 [51nod1201] 整数划分 V2 [51nod1259]的更多相关文章
- 【LeetCode题解】7_反转整数
目录 [LeetCode题解]7_反转整数 描述 方法一 思路 Java 实现 类似的 Java 实现 Python 实现 方法二:转化为求字符串的倒序 Java 实现 Python 实现 [Leet ...
- pick定理:面积=内部整数点数+边上整数点数/2-1
//pick定理:面积=内部整数点数+边上整数点数/2-1 // POJ 2954 #include <iostream> #include <cstdio> #include ...
- 转:int整数除以int整数一定得到的是int整数(易忽视)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/u014053368/article/de ...
- 题解 [51nod1201] 整数划分
题面 解析 首先,因为是不同的数字, 可以从小到大依次枚举加上每一个数字的贡献,再枚举每个数. 然而这样会T掉... 考虑到\(n\)只有\(50000\), 当分成的数最多时,设最大的数为\(m\) ...
- 51nod1201 整数划分
01背包显然超时.然后就是一道神dp了.dp[i][j]表示j个数组成i的方案数.O(nsqrt(n)) #include<cstdio> #include<cstring> ...
- 力扣(LeetCode)整数形式的整数加法 个人题解
对于非负整数 X 而言,X 的数组形式是每位数字按从左到右的顺序形成的数组.例如,如果 X = 1231,那么其数组形式为 [1,2,3,1]. 给定非负整数 X 的数组形式 A,返回整数 X+K 的 ...
- 洛谷 题解 P1025 【数的划分】
将n个小球放到k个盒子中的情况总数 = (a)至少有一个盒子只有一个小球的情况数 + (b)没有一个盒子只有一个小球的情况数 这样写出表达式: a.因为盒子不加区分,那么=情况数与"将n-1 ...
- 题解0005:数的划分(洛谷P1025)
题目描述:将整数 n 分成 k 份,每份不能为空,颠倒顺序的被看成一种分法. 题目链接:https://www.luogu.com.cn/problem/P1025 题目思路:深搜剪枝,规定搜索的下一 ...
- 编写一个js函数,该函数有一个n(数字类型),其返回值是一个数组,该数组内是n个随机且不重复的整数,且整数取值范围是[2,32]
首先定义个fn用来返回整数的取值范围: function getRand(a,b){ var rand = Math.ceil(Math.random()*(b-a)+a); return rand; ...
随机推荐
- JavaScript中的类方法、对象方法、原型方法
类方法:也叫函数方法,在JavaScript中函数也是一个对象,所以可以为函数添加属性以及方法: 对象方法:包括构造函数中的方法以及其原型上面的方法: 原型方法:一般用于对象实例共享,在原型上面添加该 ...
- 面试官问我,为什么老司机建议MySQL列属性尽量用 NOT NULL ?
本文阅读时间大约6分钟. 其实写这篇文章,也是来自一个知识星球读者的提问,他在二面的过程中被问到了,由于他简历中写道有 MySQL 调优经验,但这个问题没有回答好,二面被刷了. 其实我们刚学习 C 语 ...
- 生成Uuid工具类
package com.freeter.util; import java.util.UUID; /** * @author liuqi * **/public class Uuid{ public ...
- Django ORM (二) 增加操作
数据库表结构生成完毕后,可以使用工具连接上去 在 app01_author 表创建基础记录 在 app01_publisher 表创建基础记录 添加 data_oper 方法 在 urls.py 文件 ...
- 【Spring Cloud】Spring Cloud之自定义@SpringCloudProfile注解实现@Profile注解的功能
一.为什么会想到定义@SpringCloudProfile这样的注解 首页提一下@Profile注解:它主要用与Spring Boot多环境配置中,指定某个类只在指定环境中生效,比如swagger的配 ...
- AMQP与RabbitMQ
转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10922433.html 一:AMQP是什么 AMQP(Advanced Message Queuing Pr ...
- Linux高级运维 第八章 部署docker容器虚拟化平台
8.1 Docker概述 实验环境: CENTOS7.4-63 64位 Dcoker概述 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到 ...
- Git 游离的HEAD detached HEAD git reflog 查看所有提交的 id
- $('xx')[0].files[0]
①首先得明白jQuery对象只能使用jQuery对象的属性和方法,JavaScript对象只能使用JavaScript对象的属性和方法: ②files[0]是JavaScript的属性: ③$('xx ...
- nodemcu固件的烧录及lua开发
一.板子介绍 NodeMCU 1.0/ESP 8266 12E 该模块是安信可公司生产的,并且提供全部开发资料. 对该模块的开发有两种方式: 一种是基于乐鑫官方推出的SDK开发包在 安信可ESP的一体 ...