HDU 4588 Count The Carries 数位DP || 打表找规律
Posted mfmdaoyou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 || 打表找规律的主要内容,如果未能解决你的问题,请参考以下文章