题目有一定的难度,但是按照紫书的分析思路就会很好做了。首先在于明白一点,出现0与0之间的顺序颠倒以及出现1与1之间的顺序变化其实对最终的结果是没有影响的,那么后续的分析就围绕1与0之间的串扰。代码中首先是将输入的整数进行二进制的表示,然后分别记录0以及1在数组中的位置,其实也就是相应的发射时间。对于0串中特定的某个0以及1串中特定的某个1,我们就要判断是否0可以在1之前发射以及1是否可以在0之前发射,然后就按照贪心算法,要得到最小的值,那么我们就尽量的早发射0,这样越多的0占据高位,那么对应的数字也就越小,同时要得到最大的值,我们就今早地发射1,这样越多的1占据高位,那么所对应的数字也就越大, 上述分为两个过程来进行计算。在上述计算结束之后,就利用紫书上递推的思想,以0和1的发射顺序为基础,进行相应的状态更新即可,具体首先见如下代码:
#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<sstream>
#include<cstdio>
#include<deque>
using namespace std;
const int max_n = 64;
typedef long long LL;
LL n, d;
LL data[max_n];
LL One[max_n], Zero[max_n];
LL zAmount, oAmount;
unsigned long long min_v, max_v;
unsigned long long result[max_n][max_n],k;
bool getOne(int i,int j){//One,Zero
return (i < oAmount) && (j == zAmount || Zero[j] + d >= One[i]);
}
bool getZero(int i,int j){//One,Zero
return (j < zAmount) && (i == oAmount || One[i] + d >= Zero[j]);
}
void getMaxMin(){
int i = 0, j = 0;
while (i < oAmount || j < zAmount){
if (getZero(i, j)){
min_v = min_v * 2;
j++;
}
else{
min_v = min_v * 2 + 1;
i++;
}
}
i = 0, j = 0;
while (i < oAmount || j < zAmount){
if (getOne(i, j)){
max_v = max_v * 2 + 1;
i++;
}
else{
max_v = max_v * 2;
j++;
}
}
}
void solve(){//i 1,j 0
memset(result, 0, sizeof(result));
result[0][0] = 1;
for (int i = 0; i <= oAmount; i++){
for (int j = 0; j <= zAmount; j++){
if (getOne(i, j)) result[i + 1][j] += result[i][j];
if (getZero(i, j)) result[i][j + 1] += result[i][j];
}
}
cout << result[oAmount][zAmount] << " " << min_v << " " << max_v << endl;
}
int main(){
int kcase = 1;
while (cin >> n&&n){
cin >> d >> k;
min_v = 0;
max_v = 0;
for (int i = 0; i < n; i++){
data[n - 1 - i] = k % 2;
k = k / 2;
}
zAmount = 0;
oAmount = 0;
for (int i = 0; i < n; i++){
if (data[i] == 0){
Zero[zAmount++] = i;
}
else{//data[i]==1
One[oAmount++] = i;
}
}
cout << "Case " << kcase++ << ": ";
getMaxMin();
solve();
}
return 0;
}