USACO / Stringsobits (DP构造/康托展开)

Stringsobits



[
描述

考虑排好序的N(N<=31)位二进制数。

你会发现,这很有趣。因为他们是排列好的,而且包含所有可能的长度为N且含有1的个数小于等于L(L<=N)的数。

你的任务是输出第I(1<=I<=长度为N的二进制数的个数)大的,长度为N,且含有1的个数小于等于L的那个二进制数。

格式

PROGRAM NAME: kimbits

INPUT FORMAT:

(file kimbits.in)

共一行,用空格分开的三个整数N,L,I。

OUTPUT FORMAT:

(file kimbits.out)

共一行,输出满足条件的第I大的二进制数。

SAMPLE INPUT

5  3  19

SAMPLE OUTPUT

10011

 

分析:

这道题算比较经典的DP+构造了吧~~~
 
一开始想着暴力判断 O(logN*N),枚举N*判断每一个数1的个数logN。这种显然超时的算法下次就不要想了= =!。。。(每次想好前先给我算一下复杂度!)
 
然后就考虑DP吧~~~
 

先用动态规划计算:长度为I的01串,1的个数不大于j的有多少个

其状态表示为f[i,j]

方程:f[i,j]=f[i-1,j]+f[i-1,j-1]; //分别表示在当前位加上0和加上1时的两种状况

边界:f[i,0]=1,f[0,i]=1;

然后就是用字符串构造了(注:这里的K就是原题中的I)

第一次,寻找1的最高位位置,从次高位开始扫描(i:=n-1 downto 1),看从当前位到最后,1的个数是否超过L,比较f[i,l]与K的大小。如果f[i,l]>=K这一位不为1(因为满足条

件的前K个都是由后面i-1位01串构成的),向S加入一个‘0’补足高位;否则,找到了第一个f[i,l]<k(也就是说,仅由当前i位已经无法构成足至少k个满足条件的01串),这是

第i+1位必须添上一个‘1’,然后dec(k,f[i,l]),dec(l)

然后按照相同的方法继续构造接下来的‘1’,不足的全部由‘0’补足 

 

PS:这个算法标准化应该就是康托展开 吧~~~O.O。。。

 

代码:

 1 /*
 2 ID:138_3531
 3 LANG:C++
 4 TASK:kimbits
 5 */
 6 
 7 
 8 #include <fstream>
 9 #include <iostream>
10 #include <cstdio>
11 #include <cstdlib>
12 #include <cmath>
13 #include <iomanip>
14 #include <climits>
15 #include <vector>
16 #include <set>
17 #include <bitset>
18 #include <map>
19 #include <algorithm>
20 #include <string>
21 #include <cstring>
22 
23 
24 using namespace std;
25 
26 
27 int MAX(int a,int b)
28 {
29     return a>b?a:b;
30 }
31 
32 
33 int MIN(int a,int b)
34 {
35     return a>b?b:a;
36 }
37 
38 
39 ifstream fin("kimbits.in");
40 ofstream fout("kimbits.out");
41 
42 
43 int a[33];
44 long long f[33][33];
45 
46 
47 int main()
48 {
49     long long n,l,I;
50     fin>>n>>l>>I;
51 
52 
53     memset(f,0,sizeof(f));
54     for (int i=0;i<=n;i++)       //initialize array f
55     {
56         f[0][i]=1;
57         f[i][0]=1;
58     }
59 
60 
61     for (int i=1;i<=n;i++)          //preprocess array f
62         for (int j=1;j<=l;j++)
63         {
64             f[i][j]=f[i-1][j]+f[i-1][j-1];
65             //cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
66         }
67 
68 
69     int k=l;
70     memset(a,0,sizeof(a));
71 
72 
73     for (int i=n-1;i>=0;i--)
74     {
75         if (f[i][k]>=I)
76             a[i+1]=0;
77         else
78         {
79             a[i+1]=1;
80             I-=f[i][k];
81             k--;
82         }
83 
84 
85     }
86 
87 
88     for (int i=n;i>=1;i--)
89         fout<<a[i];
90     fout<<endl;
91 
92 
93     return 0;
94 }

转载于:https://www.cnblogs.com/AbandonZHANG/archive/2012/07/17/2598260.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值