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 }

 

posted @ 2018-02-23 10:16  Leohh  阅读(238)  评论(0编辑  收藏  举报