usaco3.2:kimbits

 对这道题地思路比较陌生,看解答之后消化了很久才弄懂,所以写个详细的笔记。

 

 题目大意是输入3个整数 N,L,I,N表示2进制串的长度,L表示这个串中最多有多少个1,I表示要求的串的序列(把所有符合要求的串从小到大排序,取第I个)。

 最朴素的办法就是从0开始枚举,把所有遇到数t,求它的位数,若满足要求就把I-1,当I==0时退出,此时记录的t为所求。

 但是数据范围最大涉及到32位的枚举,这么做会超时。(不过用朴素的解法来理解题意还是很方便的)。

 

 既然不能一一枚举,那我们就要用集合的观点来解决,若能确定属于哪个集合,用最笨的办法在集合中枚举,效率也会高很多(为神马我总是想枚举啊....),当然有更好更方便的办法。

 

 划分集合是最关键的步骤,1的个数多的不一定就比1个数少得序列排后(如1000和11),所以我们要用2维来描述这个集合。

 定义f[i,j]为长度为i,最多不超过j个1(这样定义j可以不考虑j>i的情况),的串的个数,可以根据f[i,j]来划分集合。

 根据定义 有

 f[i][j] = f[i-1][j-1] + f[i-1][j] (到这里可以发现有dp的味道了)

 其中f[i-1][j-1]的实际意义为 在f[i-1][j-1]所确定的集合中的串前面(‘数’都用‘串’代替,好理解)添加1;

 类似的f[i-1][j]表示在前面添加0。

 同时由定义可以知道 对任意 i1,j1<i2,j2 集合f[i1][j1]是f[i2][j2]的真子集。

 对于初始的f[N][L],如果有f[N][L-1]>=I (保证有解,所以必有f[N][L]>=I) I肯定是属于f[N][L-1]集合的,

 继续这样分析,直到f[N][k]>=I f[N][k-1]<I , 那么可以知道这个序号为I的串应该有k个1,因为它属于f[N][k]但不属于f[N][k-1] 这2个集合的交集就是表示仅有k个1的集合。

 

 接下来我们要确定这些1的位置。可以用同样地办法,若I串的首位1出现在第N位,F[N][l]>=I,F[N-1][l]<I。

 也就是说第N位为1的串可以用 F[N][l] - F[N-1][l]来表示,换句话说,确定该位的1后,剩下的串应该在集合f[i-1][l-1]中。如果F[N-1][l]也>I,就向更小的集合F[N-1][l]查找,所求串应该是这个集合中的第I位,同时N位补0。

 

 确定l和确定各位上的数可以合并为1个步骤。因为L本身的定义就让f[N][L]包含了f[N][l],不必知道l的具体值,只要依次确定每一位上的数即可,事实上当这一过程完成后,l的值当然也确定了。

 到现在为止,该题的算法已经讨论清楚,每一位都可以这样递归求解。

 

 不过因为几个细节问题我还是wa了几次。

 首先递归边界应该是i=0时跳出,即已经把N~1位的情况都确定了,整个过程就可以结束,而不是当I=0时跳出,因为I在是表示剩下的串在当前集合中的序列,不是表示它前面还有多少个序列.

 第二是f[i][j]的初始化, 由于其定义,不必考虑j>i的问题 ,所以

for(i=0;i<=32;i++)f[0][i]=1;

 

for(i=1;i<=32;i++)

for(j=0;j<=32;j++)

{ if(j==0)f[i][j]=1; else f[i][j]=f[i-1][j]+f[i-1][j-1];

这样就足够了,我写得时候过多的在意j>i的问题,写得很复杂,还出错.

 

拓展:

 把f[i][j]所表示的集合再细分,会发现更多的东西。

 f[i][j]定义:为长度为i,最多不超过j个1的串的个数。

 在定义

 c[i][j]: 长度为i,拥有j个1的串的个数。

 f[i][j]=c[i][0]+...+c[i][j]。 

 其实c[i][j]就是组合数(在i个位置放j个1的方法数,此时应该注意j>i的问题,组合数不允许这样)。

通过 f[i][j]=f[i-1][j-1] + f[i-1][j] 可以得出 c[i-1][j]+c[i-1][j-1]=c[i][j] 的公式,

所以这道题dp方程的推导有一个组合证明。

 

本文使用Blog_Backup未注册版本导出,请到soft.pt42.com注册。

转载于:https://www.cnblogs.com/eggeek/archive/2011/10/10/2281163.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值