Codeforces 914C Travelling Salesman and Special Numbers:数位dp
题目链接:http://codeforces.com/problemset/problem/914/C
题意:
对数字x进行一次操作,可以将数字x变为x在二进制下1的个数。
显然,一个正整数在进行了若干次操作后一定会变成1。
给定n,k(n用二进制表示给出,n <= 2^1000)。
问你有多少不超过n的正整数,将它们变为1所需的操作次数恰好为k。
题解:
由于n <= 2^1000,所以任何不超过n的数在进行了一次操作后,一定不超过1000。
所以先统计出1000以内所有数变成1所需的操作次数:f[i] = f[cal_bit(i)] + 1
那么最终答案 = ∑(恰好包含i个1,且不超过n的数字个数),其中f[i] == k-1。
所以接下来就要求恰好包含i个1,且不超过n的数字个数:
表示状态:
dp[i][j][0/1]表示已经填了前i位数,用了j个1,是否与n匹配(0/1),此时的方案数。
(n的最高位为第1位)
找出答案:
恰好包含i个1,且不超过n的数字个数 = dp[n][i][0] + dp[n][i][1]
如何转移:
对于dp[i][j][0]:
dp[i+1][j][0] += dp[i][j][0]
dp[i+1][j+1][0] += dp[i][j][0]
对于dp[i][j][1]:
如果n的第i+1位为1:
dp[i+1][j][0] += dp[i][j][1]
dp[i+1][j+1][1] += dp[i][j][1]
否则:
dp[i+1][j][1] += dp[i][j][1]
边界条件:
dp[1][0][0] = dp[1][1][1] = 1
最后统计下答案就好。
其中k==0或1的情况要特判。
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 1005 5 #define MOD 1000000007 6 7 using namespace std; 8 9 int n,k; 10 int ans=0; 11 int a[MAX_N]; 12 int f[MAX_N]; 13 int dp[MAX_N][MAX_N][2]; 14 string s; 15 16 void read() 17 { 18 cin>>s>>k; 19 n=s.size(); 20 for(int i=0;i<n;i++) a[i+1]=s[i]-'0'; 21 } 22 23 int cal_bit(int x) 24 { 25 int cnt=0; 26 int lowbit=x&-x; 27 while(lowbit) 28 { 29 cnt++; 30 x^=lowbit; 31 lowbit=x&-x; 32 } 33 return cnt; 34 } 35 36 void cal_f() 37 { 38 f[1]=0; 39 for(int i=2;i<=1000;i++) 40 { 41 f[i]=f[cal_bit(i)]+1; 42 } 43 } 44 45 void cal_dp() 46 { 47 memset(dp,0,sizeof(dp)); 48 dp[1][0][0]=dp[1][1][1]=1; 49 for(int i=1;i<n;i++) 50 { 51 for(int j=0;j<=i;j++) 52 { 53 if(dp[i][j][0]) 54 { 55 dp[i+1][j][0]+=dp[i][j][0]; 56 dp[i+1][j+1][0]+=dp[i][j][0]; 57 dp[i+1][j][0]%=MOD; 58 dp[i+1][j+1][0]%=MOD; 59 } 60 if(dp[i][j][1]) 61 { 62 if(a[i+1]) 63 { 64 dp[i+1][j][0]+=dp[i][j][1]; 65 dp[i+1][j+1][1]+=dp[i][j][1]; 66 dp[i+1][j][0]%=MOD; 67 dp[i+1][j+1][1]%=MOD; 68 } 69 else 70 { 71 dp[i+1][j][1]+=dp[i][j][1]; 72 dp[i+1][j][1]%=MOD; 73 } 74 } 75 } 76 } 77 } 78 79 void cal_ans() 80 { 81 for(int i=1;i<=1000;i++) 82 { 83 if(f[i]==k-1) 84 { 85 ans+=dp[n][i][0]+dp[n][i][1]; 86 ans%=MOD; 87 } 88 } 89 } 90 91 void work() 92 { 93 if(k==0) 94 { 95 cout<<1<<endl; 96 return; 97 } 98 cal_f(); 99 cal_dp(); 100 cal_ans(); 101 if(k==1) cout<<ans-1<<endl; 102 else cout<<ans<<endl; 103 } 104 105 int main() 106 { 107 read(); 108 work(); 109 }