原题连接:Click

加强数据:Click

Solution

参考博客:Click

题目意思非常明确了,这是一道组合数学的题目。我就直接讲dp解法了。

dp

题意可以转化为将\(n\)个苹果放进\(k\)个盒子里,并且不允许空盒。

设\(f[i][j]\)代表将\(i\)个苹果放入\(j\)个盒子中,那么我们用解决这类问题的常用方法来分析:

我们必须先保证每个盒子非空,因此在\(i\)个苹果中选出\(j\)个放入每个盒子。

此时我们剩余\(i-j\)个苹果,我们就是要往已有的一层苹果上加\(i-j\)苹果,求此时的方案数。

现在\(i-j\)个苹果可以任意分配了,也就是分成\(1\)份、\(2\)份、\(3\)份都是合法的……

得到转移方程:

\[dp[i][j] = \sum_{k=1}^jdp[i-j][k]
\]

枚举\(i\),随后枚举\(j\),随后枚举\(k\),三层循环即可得出答案。

时间复杂度为\(O(nk^2)\),预期得分70分。

这个或许可以套树状数组优化一下求和……

那么复杂度是\(O(nk\log k)\),然而最大的范围\(nk\)达到了\(1.2\)亿的大小,再加上个\(\log\)铁定超时。

然后你可以发现:

\[dp[i-1][j-1] = \sum_{k=1}^{j-1}dp[i-j][k]
\]

为什么会有这样的奇特之处呢?因为\(i-j\)就是\(i\)和\(j\)的差值,那么同增同减一个\(1\),dp数组的一维下标是不变的,只是二维的\(k\)会少一个\(dp[i-j][j]\),那么我们把这个加上就好了。

据此写出转移方程:

\[dp[i][j] = dp[i-1][j-1] + dp[i-j][j]
\]

两层循环即可转移,复杂度就降到\(O(nk)\)了,由于常数小,可以通过本题。

但交上去……MLE!

空间优化

空间复杂度也是\(O(nk)\)的,但事实上我们只需要用到\(O(k^2)\)的内容,很容易想到滚动数组。

于是写出:

inline int pos(const int &x)
{
return (x % 600) + 1;
}
int main()
{
scanf("%d%d", &n, &k);
dp[pos(0)][0] = 1;
int i, j;
for (i = 1; i <= n; ++i)
{
memset(dp[pos(i)], 0, sizeof(dp[pos(i)]));
for (j = 1; j <= k && j <= i; ++j)
dp[pos(i)][j] = (dp[pos(i-j)][j] + dp[pos(i-1)][j - 1]) % 10086;
}
printf("%d", dp[pos(n)][k]);
return 0;
}

个人预期是能AC了,但实际上……第15个点冷酷无情地T了。

评测机跑得不够快

拯救TLE

吸了氧还是不能拯救世界之后,我想起了当年用的一种奇淫技巧……

显然此时TLE完全是常数问题,将内层循环的两个判断改成取min逆序后依然无法通过。

常数影响最大的就是pos函数了,于是改成了指针映射,成功AC!

指针映射

我们考虑要如何避免pos函数的高耗时,当然想到了预处理。预处理一遍pos数组,直接访问即可,这应该也是能卡过的(没有尝试)。

但还有一种更有技巧性、效率更高的方法:指针。

开一个f数组,如下:

int *f[maxn];

然后赋值:

f[i] = dp[pos(i)];

那么访问时,直接:

f[i][j] = ....

为什么会快?这个很显然了吧……事实上,这种方法比:

dp[pos[i]][j] = ....

要快上不少,为什么?

因为\(f[i]\)存的索引直接加上\(j\)就能得到地址,我们实际上避免了两个大数的乘法,而使其变成了加法。

举例:

原先访问方式:

dp[x∗(m+2)+y]

进行了一次乘法一次加法

解析一下就是:

return dp + (x * (m+2) + y);

而现在的访问方式:

(f[x]+y)

解析一下就是:

return (f + x) + y;

效率提升相当显著。

以上这段是直接copy原来那篇树上背包的优化中的内容……

同时注意我们的预处理方式:

int pointer = 0;
++pointer;
if(pointer >= 600)
pointer -= 600;

可以避免反复求余的预处理效率损失。

最后第15个点跑了500ms左右……

Code

#include <cstdio>
#include <cstring>
using namespace std;
int n, k;
int dp[610][610];
int *f[200100];
inline int min(const int &a,const int &b){return a<b?a:b;}
int main()
{
scanf("%d%d", &n, &k);
int p = 0;
for (int i = 0; i <= n; ++i)
{
if (p >= 600)
p -= 600;
f[i] = dp[p + 1];
++p;
}
f[0][0] = 1;
int i, j;
for (i = 1; i <= n; ++i)
{
memset(f[i], 0, sizeof(f[i]));
for (j = min(k,i); j; --j)
f[i][j] = (f[i - j][j] + f[i - 1][j - 1]) % 10086;
}
printf("%d", f[n][k]);
return 0;
}

[LuoguP1025][数据加强]数的划分的更多相关文章

  1. luoguP1025+codevs 1039 数的划分 x

    luoguP1025 + codevs1039 数的划分 2001年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold   题目描述 Des ...

  2. C语言 · 数的划分

    算法训练 数的划分   时间限制:1.0s   内存限制:256.0MB        锦囊1 使用动态规划. 锦囊2 用F[i,j,k]表示将i划分成j份,最后一份为k的方案数,则F[i,j,k]= ...

  3. dp练习(8)——数的划分

    1039 数的划分 2001年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 将整数 ...

  4. codevs——1039 数的划分

    1039 数的划分 2001年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 将整数 ...

  5. Java实现 蓝桥杯VIP 算法提高 数的划分

    算法提高 数的划分 时间限制:1.0s 内存限制:256.0MB 问题描述 一个正整数可以划分为多个正整数的和,比如n=3时: 3:1+2:1+1+1: 共有三种划分方法. 给出一个正整数,问有多少种 ...

  6. Oracle 查询库中所有表名、字段名、字段名说明,查询表的数据条数、表名、中文表名、

    查询所有表名:select t.table_name from user_tables t;查询所有字段名:select t.column_name from user_col_comments t; ...

  7. 查看SqlAzure和SQLServer中的每个表数据行数

    SqlAzure中的方式: select t.name ,s.row_count from sys.tables t join sys.dm_db_partition_stats s ON t.obj ...

  8. jquery通过ajax获取数据,控制显示的数据条数

    效果图: 现在我们可以先看它的json数据,如图所示:                然后可以对应我们的代码进行理解. jquery通过ajax获取数据,并通过窗口大小控制显示的数据条数,以及可以根据 ...

  9. NOIP2001 数的划分

    题二 数的划分(20分) 问题描述 将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序). 例如:n=7,k=3,下面三种分法被认为是相同的. 1,1,5; 1,5,1; 5,1,1; 问 ...

随机推荐

  1. Linux 一些有用的能力

    编程能力 Linux产生于一群真正的黑客.尽管人们习惯于认为Linus是Linux的缔造者,在linux包含的数以千计的文件中,也有一个名为Credits的文件记录了主要的Linux Hacker们的 ...

  2. ES5-bind用法及与以前的apply和call

    当我们调用一个函数的时候,函数中的this一般是指向调用者的.但是我们其实可以在调用函数的时候,传入一个对象,让函数中的this指向我们传入的对象,而不是调用者本身. apply,call,bind都 ...

  3. 【转】弹出USB大容量存储设备时出问题的解决方法

    原文链接 如下图所示,这个问题,相信很多人都有遇到过,而且经常难以解决,试了很多方法都无效.到最后,只能抱着侥幸的心理直接拔出,如果运气好,可能没有事,如果运气不好,你的U盘或者移动硬盘就要从此报废了 ...

  4. 【原】简单shell练习(六)

    1.shell获取进程号并杀掉该进程 kill - $(ps -ef | grep node| grep -v grep | awk '{print $2}') 解析: ps (processStat ...

  5. CF908D 【New Year and Arbitrary Arrangement】

    蒟蒻渣渣禹小心翼翼发布题解.... 这道题,嗯,期望,dp,好,我们有思路了.... however, 主要问题在于字符串无限延伸,so,我们需要考虑记录前缀的关键量来为DP设置终止状态. 我们不妨设 ...

  6. 操作系统OS - 同步和异步,阻塞和非阻塞

    同步和异步关注的是消息通信机制,阻塞/非阻塞是程序在等待调用结果(消息,返回值)时的状态

  7. 记一次RocketMQ源码导入IDEA过程

    首先,下载源码,可以官网下载source包,也可以从GitHub上直接拉下来导入IDEA.如果是官网下载的source zip包,直接作为当前project的module导入,这里不赘述太多,只强调一 ...

  8. linux 添加与修改用户归属组

    参考资源:https://cnzhx.net/blog/linux-add-user-to-group/ 一:已存在的用户 1.要以root进行登录 2.打开终端 3.修改分组 usermod -a ...

  9. Eclipse上将maven项目部署到tomcat,本地tomcat下,webapps下,web-inf下lib下没有jar包决绝方案

    右键项目选择properties  ,选择Deployment Assembly  , 右边点击Add 然后选择  Java Build Path Entries ,,,next  ,  选择Mave ...

  10. Netsparker破解版5.3 Netsparker Enterprise 5.3.0.24388[cracked]

    Netsparker破解版5.3 Netsparker Enterprise 5.3.0.24388[cracked]该版本更新时间为2019年7月8日下载地址:1 https://www.dr-fa ...