首页 > 代码库 > HDU 4588 Count The Carries 数位DP || 打表找规律

HDU 4588 Count The Carries 数位DP || 打表找规律

2013年南京邀请赛的铜牌题。。。做的非常是伤心。另外有两个不太好想到的地方。。

。。a 能够等于零,另外a到b的累加和比較大。大约在2^70左右。


首先说一下解题思路。

首先统计出每一位的1的个数,然后统一进位。

设最低位为1。次低位为2,依次类推,ans[]表示这一位上有多少个1。那么有

sum += ans[i]/2,ans[i+1] += ans[i]/2;

sum即为答案。

好了,如今问题转化成怎么求ans[]了。


打表查规律比較奇妙,上图不说话。

技术分享


打表的代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <stack>
#include <map>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 9999991
#define lowbit(x) (x&(-x))

using namespace std;

void out(int x)
{
    while(x)
    {
        printf("%d",x&1);
        x >>= 1;
    }
}

int main()
{
    for(int i = 1;i <= 100; ++i)
    {
        printf("i = %3d : ",i);
        out(i);
        puts("");
    }

    return 0;
}


好了。重头戏来了。这个数位DP做的还是非常有成就感的。

记忆化搜索 + bfs推送

昨天事实上想这个思路了。

只是当时比較着急没写出来,心态是个非常重要的东西。

记忆化搜索部分dp[sta][site][dig] 第一维表示是否到达上限,site表示第几位,dig表示是1还是0。

那么site == 1时,也就是说最高位记录的信息是正确的。可是其它的就不是想要的了。

由于dp[sta][site][sta] 表示当前位到最低位的方案数。

所以说要把最高位的答案推送下去。

推送非常easy,每种状态最多仅仅会由两种子状态得到,所以按比例推送一下即可了。

详见代码。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <cmath>
#include <stack>
#include <map>

#pragma comment(linker, "/STACK:1024000000");
#define EPS (1e-8)
#define LL long long
#define ULL unsigned long long
#define _LL __int64
#define _INF 0x3f3f3f3f
#define Mod 9999991
#define lowbit(x) (x&(-x))

using namespace std;

_LL dp[2][105][2],dp1[2][105][2];

_LL up[105];

_LL ansa[105],ansb[105];

_LL dfs(_LL sta,_LL site,_LL dig)
{
    if(dp[sta][site][dig] != -1)
        return dp[sta][site][dig];
    if(site == 100)
    {
        dp[sta][site][dig] = 1;
        return 1;
    }

    dp[sta][site][dig] = 0;

    if(sta == 0)
    {
        dp[sta][site][dig] = dfs(0,site+1,0) + dfs(0,site+1,1);
    }
    else
    {
        if(up[site+1] == 1)
        {
            dp[sta][site][dig] = dfs(1,site+1,1) + dfs(0,site+1,0);
        }
        else
        {
            dp[sta][site][dig] = dfs(1,site+1,0);
        }
    }
    return dp[sta][site][dig];
}

void Cal(_LL x,_LL *ans)
{
    _LL temp = x,len = 100;

    memset(ans,0,sizeof(_LL)*(102));

    if(x == 0)
        return ;
    while(temp)
    {
        up[len--] = temp%2;
        temp /= 2;
    }

    len++;
    memset(dp,-1,sizeof(dp));
    dfs(1,len,1);
    dfs(0,len,0);
    _LL i;

    memset(dp1,0,sizeof(dp1));

    dp1[1][len][1] = max((_LL)0,dp[1][len][1]);
    dp1[1][len][0] = max((_LL)0,dp[1][len][0]);
    dp1[0][len][1] = max((_LL)0,dp[0][len][1]);
    dp1[0][len][0] = max((_LL)0,dp[0][len][0]);

    for(_LL site = len;site <= 99; ++site)
    {
        dp1[0][site+1][1] += dp1[0][site][1]*dp[0][site+1][1]/(dp[0][site+1][1] + dp[0][site+1][0]);
        dp1[0][site+1][1] += dp1[0][site][0]*dp[0][site+1][1]/(dp[0][site+1][1] + dp[0][site+1][0]);
        dp1[0][site+1][0] += dp1[0][site][1]*dp[0][site+1][0]/(dp[0][site+1][1] + dp[0][site+1][0]);
        dp1[0][site+1][0] += dp1[0][site][0]*dp[0][site+1][0]/(dp[0][site+1][1] + dp[0][site+1][0]);

        if(up[site+1] == 1)
        {
            if(up[site] == 1)
            {
                dp1[1][site+1][1] += dp1[1][site][1]*dp[1][site+1][1]/(dp[1][site+1][1] + dp[0][site+1][0]);
                dp1[0][site+1][0] += dp1[1][site][1]*dp[0][site+1][0]/(dp[1][site+1][1] + dp[0][site+1][0]);
            }
            else
            {
                dp1[1][site+1][1] += dp1[1][site][0]*dp[1][site+1][1]/(dp[1][site+1][1] + dp[0][site+1][0]);
                dp1[0][site+1][0] += dp1[1][site][0]*dp[0][site+1][0]/(dp[1][site+1][1] + dp[0][site+1][0]);
            }
        }
        else
        {
            dp1[1][site+1][0] = dp1[1][site][0] + dp1[1][site][1];
        }
    }

    for(i = len;i <= 100; ++i)
    {
        ans[i] = max((_LL)0,dp1[1][i][1]) + max((_LL)0,dp1[0][i][1]);
    }
}

int main()
{
    _LL a,b,i;

    while(scanf("%I64d %I64d",&a,&b) != EOF)
    {
        Cal(b,ansb);
        Cal(max((_LL)0,a-1),ansa);

        _LL sum = 0;

        for(i = 100;i >= 1; --i)
        {
            sum += (ansb[i]-ansa[i])/2;
            ansb[i-1] += (ansb[i]-ansa[i])/2;
        }
        printf("%I64d\n",sum);
    }

    return 0;
}



HDU 4588 Count The Carries 数位DP || 打表找规律