这题证明俺现在真是老了,退步了,这是第一份代码:
-
#include <fstream>
-
using namespace std;
-
-
ifstream fin("kimbits.in");
-
ofstream fout("kimbits.out");
-
-
unsigned int N, L, pos;
-
-
int main()
-
{
-
fin >> N >> L >> pos;
-
-
unsigned int num = 0, count = 0, max = (1<<N)-1, bit1, tmp;
-
while(count < pos)
-
{
-
if(num > max) break;
-
bit1 = 0, tmp = num;
-
for(; tmp > 0; bit1++)
-
tmp &= (tmp - 1);
-
if(bit1 <= L) ++count;
-
++num;
-
}
-
-
num -- ;
-
for (int i = N-1; i >=0; --i)
-
fout << ((num >>i) & 0x1u);
-
fout << endl;
-
return 0;
-
}
其实还是挺简洁的哈,就是会超时,因为基本思路是暴力搜索。

这个是很郁闷的,看数据,确实太大了。仔细端详一番,觉得有DP的感觉,例如,对于(二进制)二位数:10 11,要得到三位数,实际上就是移位和移位+1,+1的操作中可以判断+1后得到的数字中的1是不是超过了给定的L,不过,首先还是先崩溃了一下:
-
#include <fstream>
-
#include <deque>
-
#include <vector>
-
using namespace std;
-
-
ifstream fin("kimbits.in");
-
ofstream fout("kimbits.out");
-
-
typedef unsigned int UINT;
-
UINT N, L, pos;
-
vector<deque<UINT> > dp;
-
-
inline int printBinary(UINT num)
-
{
-
for (int i = N-1; i >=0; --i)
-
fout << ((num >>i) & 0x1u);
-
fout << endl;
-
return 0;
-
}
-
-
int main()
-
{
-
fin >> N >> L >> pos;
-
-
if(pos <= 2) {
-
printBinary(pos-1);
-
return 0;
-
}
-
-
deque<UINT> d, dPre;
-
d.push_back(0);
-
for(UINT i = 0; i <= N; ++i)
-
dp.push_back(d);
-
-
dp[0][0] = 1, dp[0].push_back(1);
-
UINT count = 2, uTmp;
-
-
for(UINT i = 0; i < N; ++i)
-
{
-
for(UINT k = 0; k <= i; ++k)
-
{
-
d.clear();
-
for(UINT j = 1; j <= dp[k][0]; ++j)
-
{
-
uTmp = (dp[k][j] << 1);
-
if(++count == pos) return printBinary(uTmp);
-
-
if(k+2 <= L) // k从0开始的
-
{
-
uTmp = (dp[k][j]<<1)+1;
-
if(++count == pos) return printBinary(uTmp);
-
d.push_back(uTmp);
-
}
-
-
dp[k][j] <<= 1;
-
}
-
if(k == i)
-
{
-
dp[k+1].insert(++dp[k+1].begin(), d.begin(), d.end());
-
dp[k+1][0] += d.size();
-
}
-
if(k > 0)
-
{
-
dp[k].insert(++dp[k].begin(), dPre.begin(), dPre.end());
-
dp[k][0] += dPre.size();
-
}
-
dPre = d;
-
}
-
}
-
-
return 0;
-
}
时间还不知道,空间先扛不住了:

看Test 6和7,估计这个办法也不咋滴,为什么呢,究其原因还是无效计算过多。下面是AC的代码,主要算法思想写在注释里了,这道题本来很简单的,却搞了我这么久,受打击了,呜呜....
-
#include <fstream>
-
using namespace std;
-
typedef unsigned int UINT;
-
ifstream fin("kimbits.in");
-
ofstream fout("kimbits.out");
-
-
UINT N, L, pos;
-
UINT dp[33][33]; // dp(i,j)表示长度为i,1的个数不超过j的串有多少
-
int res[33]={0};
-
-
int main()
-
{
-
fin>> N >> L >> pos;
-
for(UINT j = 0; j <= L; j++)
-
dp[0][j] = 1;
-
-
// 方程:f[j,k]=f[j-1,k]+f[j-1,k-1]; 分别表示在当前位加上0和加上1时的两种状况
-
// 边界:f[j,0]=1, f[0,j]=1, f[j,k](k>j)=f[j,j]
-
// 这样我们得到了所有的f[j,k] 需要做的就是据此构造出所求字符串
-
for(UINT i = 0; i <= N; i++)
-
{
-
for(UINT j = 0; j <=L; j++)
-
{
-
if(j == 0) dp[i][j] = 1;
-
else if(j <= i) dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
-
else if(j > i) dp[i][j] = dp[i][i];
-
}
-
}
-
-
// 构造思路如下:
-
// 设所求串为S,假设S的位中最高位的1在K位
-
// 那么必然满足:
-
// F[K-1,L]<pos and F[K,L]>=pos
-
// 这样的K是唯一的, 所以S的第一个1在从右至左第K位
-
// 因为有F[K-1,L]个串第K位上为0,所以所求的第I个数的后K位就应该是:
-
// 满足"位数为K且串中1不超过L-1个"这个条件的第 pos - F[K,L] 个数 => 递归的过程
-
while(pos > 1)
-
{
-
for(int i = N-1; i>=0; i--)
-
{
-
if(dp[i][L] < pos)
-
{
-
res[N-i] = 1;
-
pos -= dp[i][L];
-
break;
-
}
-
}
-
L--;
-
}
-
UINT i = 1;
-
while(res[i] == 0 && 33 - i <= N) i++;
-
if(i == N+1) i = 1;
-
for(; i <= N; i++)
-
fout << res[i];
-
fout << endl;
-
return 0;
-
}

本文通过三次代码迭代,最终采用动态规划方法高效解决了寻找特定条件下的第pos个二进制串的问题。该问题要求二进制串长度为N,包含不多于L个1。文章详细记录了解题过程,从最初的暴力搜索到使用动态规划减少无效计算。

560

被折叠的 条评论
为什么被折叠?



