能被一个整数整除的二进制序列个数(SOJ 2009)

2009: Zeros and Ones

问题:给出一个$N$位($N\le64$且为偶数)的二进制数并且没有前导$0$,且$0$和$1$的个数都是$N/2$.给出另一个整数$K$,求解满足上述条件并且能被$K$整除的二进制数的个数。

分析:动态规划。定义$dp[i][j][k]$为前$i$位中有$j$个$1$并且对$K$取模等于$k$的二进制序列个数,则根据第$i$位是否为$1$构建状态转移方程:

(1)第$i$位为$0$,则$dp[i][j][k]=dp[i-1][j][k]$; 

(2)第$i$位为$1$,则$dp[i][j][k]=dp[i-1][j-1][x]$,这里关键是$x$的取值!前$i$位对$K$取模有两个部分:前$i-1$位和第$i$位,定义$c$为第i位对$K$的取模结果,$c=2^{N-i}\%K$,则

$(x+c)\%K=k, 0\le x<K, 0\le c<K$,

$x+c$只可能等于$k$或$K+k$.若$c\le k$, 则$x=k-c$;否则$x=K+k-c$. 

总的状态转移方程为:

$dp[i][j][k] = dp[i - 1][j][k]+(c<=k ? dp[i-1][j-1][k-c] : dp[i-1][j-1][K+k-c])$.

注意:初始化所有$dp[i][j][k]=0$,只有$dp[1][1][2^{N-1}\%K]=1$.C++中$long\ long$的最大值为$2^{63}-1$,这里是$2^{63}$不能用$long\ long$变量表示,好在是取模,可以很容易解决这个问题。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
long long dp[66][34][101];
int main()
{
    int T, N, K;
    scanf("%d", &T);
    int t;
    int i, j, k;
    int jS;
    long long a=1;
    int c;
    for (t = 1; t <= T; t++)
    {
        scanf("%d%d", &N, &K);
        if (N % 2==1 || K==0)
        {
            printf("Case %d: 0\n",t);
            continue;
        }
        memset(dp, 0, sizeof(dp));
        c = ( (a << (N - 2)) % K * (2%K) )%K;
        dp[1][1][c] = 1;
        for (i = 2; i <= N; i++)
        {
            jS = min(i, N / 2);
            c = (a << (N - i))%K;
            for (j = 1; j <= jS; j++)
                for (k = 0; k < K; k++)
                    dp[i][j][k] = dp[i - 1][j][k]+(c<=k ? dp[i-1][j-1][k-c] : dp[i-1][j-1][K+k-c]);
        }
        printf("Case %d: %lld\n", t,dp[N][N/2][0]);
    }
    return 0;
}
View Code

posted on 2019-03-29 19:02  小叶子曰  阅读(210)  评论(0编辑  收藏  举报

导航