这道题。。最开始看的时候不知道怎么做。。经过将讲解。。开始理解了。。
数位dp
状态定义大概是这样子的。。 f[i][j][k] (其实可以省略一维,不过空间足够,写起来更方便)第i个位置,用到第j个0,第i个1了。
呃。。我听了状态的定义还是不太明白。。
然后做法是这个样子的:从二进制低位到高位依次填数字(0或1),当然,填数字是有限制的。。。
考虑原数字k的二进制形势,举个例子:当11101(2)=k , d=2 的时候 最左边的1是不能够移到0的右边的,而第二个1就可以,那么构成的新的数字就是11011(2),可以看成是把1弄到了0的右边。。。也就是说,当 当前的1的位置+d > 当前0的位置 的时候 能够将1移动到0的右边,就能够构成新的数字了嘛 = =。。
另外两个问题就不讨论了。。
下面是代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL unsigned long long
using namespace std ;
LL num0 , num1 , n , d , k , T;
bool vis[200][100][100] ;
LL f[200][100][100] ;
int s0[200] , s1[200] ;
LL dfs(int p,int p0,int p1) {
//if(p == n && p0 == num0 && p1 == num1) return 1 ;
LL &x = f[p][p0][p1] ;
if(vis[p][p0][p1]) return x ;
vis[p][p0][p1] = true ;
x = 0 ; bool op = true ;
if(p0 < num0 && p1 < num1) {
if(s0[p0] + d < s1[p1]) x = dfs(p+1 , p0+1 , p1) , op = false ;
else if(s1[p1] + d < s0[p0]) x = dfs(p+1 , p0 , p1+1) , op = false ;
}
if(op) {
if(p0 < num0) x = dfs(p+1 , p0+1 , p1) ;
if(p1 < num1) x += dfs(p+1 , p0 , p1+1) ;
}
return x ;
}
int main() {
while(~scanf("%llu",&n)) {
if(!n) break ;
scanf("%llu%llu",&d,&k) ;
num0 = num1 = 0 ;
for(int i = 0 ; i < n ; i++) {
if(k&1) s1[num1++] = i ;
else s0[num0++] = i ;
k >>= 1 ;
}
memset(vis , 0 , sizeof(vis)) ;
memset(vis[n] , 1 , sizeof(vis[n])) ;
memset(f[n] , 0 , sizeof(f[n])) ;
f[n][num0][num1] = 1 ;
LL ans0 , ans1 = 0 , ans2 = 0;
ans0 = dfs(0 , 0 , 0) ;
int a = 0 , b = 0 ;
for(int i = 0 ; i < n ; i++) {
if(a < num0 && b < num1 && s0[a] + d < s1[b]) {
a++ ; continue ;
}
if(b < num1) { b++ ; ans1 |= 1llu<<i ; continue ; }
a++ ;
}
a = b = 0 ;
for(int i = 0 ; i < n ; i++) {
if(a < num0 && b < num1 && s1[b] + d < s0[a]) {
ans2 |= 1llu << i ;
b++ ; continue ;
}
if(a < num0) {a++ ; continue ;}
b++ ; ans2 |= 1llu << i ;
}
printf("Case %d: %llu %llu %llu\n",++T,f[0][0][0],ans1,ans2) ;
}
return 0 ;
}