poj 3971 Scales (dp)

题目大意:

给你一个重量为w的物品 和 重量分别为 1  2  4  ... 2^(n-1) 的砝码,问 一共有多少种方式使得天平平衡。

解题思路:

让我们求的 就是 w + x = y 并且 x 与 y 在二进制形式下没有相同位置为 1  即  x & y = 0  

一开始,我希望用数学公式推出来,但是失败了。后来查看了别人的题解,发现这是一道 动态规划。

我们设 dp[i][j] 表示 第i位,是否需要进位。并且,dp从低位往高位推比较好。

对于一个给定的 w 。如果他的第i位为0,x的第i位为0,可以选择从i+1位进位,也可以选择不进位,此时第i位均不进位;如果他的第i位为0,x的第i位为1,只能选择从i+1位进位,此时第i位进位(如果不进位,那么y的第i位也是1,与x&y=0矛盾)。同理,我们可以得到w的第i位为1,x的第i位为0的时候,可以选择从i+1位进位,此时第i位进位,也可以选择从i+1位不进位,此时第i位不进位(因为此时x的第i位为0,所以即使y的第i位也为0,x&y=0依然成立);当w的第i位为1,x的第i位为1的时候,第i位肯定进位,而且第i+1位不能进位。

除此之外,我们需要对w = 0 的时候进行特判。因为当w=0 时,答案=0;

具体,代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
// w + x = y    x & y = 0
int dp[1000009][3];
int main()
{
    int T,n,m,i,j,l,d;
    char w[1000009];
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&l,&d);
        scanf("%s",w+(n-l));
        if(w[n-l]=='0')
        {
            printf("0\n");
            continue;
        }
        for(i=0; i<(n-l); i++)//统一起见,把w的位数与给定砝码的最大位数统一。不足用0补充
            w[i]='0';
        memset(dp,0,sizeof(dp));
        dp[n][0]=1;
        for(i=n-1; i>=0; i--)
        {
            
            if(w[i]=='0')
            {
                //x的当前位为 0
                dp[i][0]+=dp[i+1][0]+dp[i+1][1];
                //x的当前位为 1
                dp[i][1]+=dp[i+1][1];
            }
            else if(w[i]=='1')
            {
                //x的当前位为 0
                dp[i][0]+=dp[i+1][0];
                dp[i][1]+=dp[i+1][1];
                //x的当前位为 1
                dp[i][1]+=dp[i+1][0];
            }
            dp[i][0]%=d;
            dp[i][1]%=d;
        }
        printf("%d\n",dp[0][0]);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/u011561033/article/details/45688179