数位DP入门题两枚

快速计算二进制中连续1的数量
本文介绍了一种高效的方法来计算给定整数转换为二进制后的连续1的数量,包括预处理和数位处理技巧。

light OJ 1140 - How Many Zeroes?  


题意: 给你两个数 m,n (m <= n)(超int),问你这两个数之间的所有数中零的个数。

思路:当然我们只要函数(f(x))计算0-x之间0的个数,那答案就是 f(n) - f(m-1)

如何完成这一函数呢?

1. 我们先预处理出dp[i][j] (表示以首位为j,数的长度为i的所有数的0的个数)

2. 数位处理:

    给你一个数  n 如 2034 数位为4

    1. 先计算数位小于4的数 (即 0 - 999)

    2. 分别对其每一位进行*降位*处理

         流程如下:

         计算 1000 - 1999

         计算  2000 - 2029

         计算  2030- 2033

         计算  2033

注意: 有0位时跳过这位的降位, 另还要注意这位的0对计算后面数的影响

题意:问你0----n(int)内所有数字转化成二进制数后总共有多少个11(相邻位都是1)。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
LL dp[22][10], dd[22];

void init()
{
	int i, j, k;
	dd[0] = 1;
	for(i = 1; i <= 21; i++)
		dd[i] = dd[i-1] * 10;
	dp[1][0] = 1;
	for(i = 2; i <= 21; i++)
		for(j = 0; j <= 9; j++)
			dp[i][j] = dp[i-1][j] * 10 + dd[i-2];
	//for(i = 0; i <= 10; i++)
	//	printf("dp[%d][%d] = %I64d\n", i, 1, dp[i][1]);
}
int a[22];
void update(LL n, int &num)
{
	num = 0;
	while(n) {a[num++] = n % 10; n /= 10; }
}
LL gao(LL n)
{
	if(n < 0) return 0;
	if(n < 10) return 1;
	int num; update(n, num);
	LL ans = 1, cnt = 0; // cnt表示处理到i时,给定的数n在第i位之前0的个数
	int i, j, k;
	//先处理数位小于num(最大数的数位)的数,如n = 1000,我们先处理 0 - 999;
	for(i = 1; i < num; i++)
		for(j = 1; j <= 9; j++)
		    ans += dp[i][j];
	//分别对其每一位进行*降位*处理
	for(i = num-1; i >= 0; i--)
	{
		if(!a[i]) {cnt++; continue; }
		for(j = 1; j <= a[i]-1; j++) //处理该位为 1- a[i]-1
		{
			ans += dp[i+1][j];   //
			ans += cnt * dd[i]; // 加上i位之前出现的0的情况
		}
		if(i != num-1) ans += dp[i+1][0] + cnt * dd[i]; // 处理该位为 0, 注意第一位不能是0
	}
	ans += cnt; // 处理n,只要加上n中的0的个数
	//printf("n = %I64d ans = %I64d\n", n, ans);
	return ans;
}
int main()
{
	int i, j, cas;
	LL n, m;
	init();
	scanf("%d", &cas);
	for(int ca = 1; ca <= cas; ca++)
	{
		scanf("%lld%lld", &m, &n);
		printf("Case %d: %lld\n", ca, gao(n) - gao(m-1));
	}
	return 0;
}
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
#define mp make_pair
pii dp[10][20];
int a[20];
int dfs(int pos, int len, int pre, bool lim, bool zero) {
    if(!len) return mp(!pos, 1);
    if(!lim && !zero) {
        if(~dp[pos][len].first) {
            if(!pre) return dp[pos][len].first+dp[pos][len].second;
            return dp[pos][len].first;
        }
    }
    ll ret = 0;
    int m = lim ? a[len] : 9;
    for(int i = 0; i <= m; i++)
        pii tp = dfs(i, len-1, i, lim&&i==m, zero&&i==0);
    if(!lim && !zero)
        dp[pos][len] = ret;
    return ret;
}
ll gao(ll x) {
    if(x < 0) return 0;
    int len = 0;
    while(x) {
        a[++len] = x%10;
        x /= 10;
    }
    return dfs(0, len, 0, 1, 1);
}
int main() {
    int cas, ca = 1;
    scanf("%d", &cas);
    memset(dp, -1, sizeof(dp));
    while(cas--) {
        ll a, b;
        scanf("%lld%lld", &a, &b);
        printf("Case %d: %lld\n", ca++, gao(b) - gao(a-1));
    }
    return 0;
}



light OJ 1032 - Fast Bit Calculation


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
int a[33];
void gao(int n, int &num)
{
    num = 0;
    while(n)
    {
        a[num++] = n % 2;
        n /= 2;
    }
}
LL dp[33][2];
 
void init()
{
    dp[1][0] = dp[1][1] = 0;
    dp[2][1] = 1; dp[2][0] = 0;
    for(int i = 3; i <= 31; i++)
    {
        dp[i][1] = dp[i-1][0] + dp[i-1][1] +  (1<<(i-2));
        dp[i][0] = dp[i-1][1] + dp[i-1][0];
    }
}
int main()
{
    int i, j, n, cas;
    init();
    scanf("%d", &cas);
    for(int ca = 1; ca <= cas; ca++)
    {
        scanf("%d", &n);
        int m; gao(n, m);
        LL ans = 0, cnt = 0;
        for(i = m-1; i >= 0; i--)
        {
            if(a[i])
            {
                ans += dp[i+1][0];
                ans += cnt * (1<<i);   
            }
            if(i+1 <= m-1 && a[i] == a[i+1] && a[i] == 1) cnt++;
        }
        ans += cnt;
        printf("Case %d: %lld\n", ca, ans);
    }
    return 0;
}


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
#define mp make_pair
pii dp[2][35];
int a[35];
pii dfs(int pos, int len, bool lim) {
    if(!len) return mp(0, 1);
    if(!lim && ~dp[pos][len].first) return dp[pos][len];
    int m = lim ? a[len] : 1;
    pii ret = mp(0, 0);
    for(int i = 0; i <= m; i++) {
        pii tp = dfs(i, len-1, lim&&i==m);
        ret.first += tp.first;
        ret.second += tp.second;
        if(pos && i) ret.first += tp.second;
    }
    if(!lim) dp[pos][len] = ret;
    return ret;
}
ll gao(ll x) {
    if(x <= 1) return 0;
    int len = 0;
    while(x) {
        a[++len] = x&1;
        x>>=1;
    }
    return dfs(0, len, 1).first;
}
int main() {
    int cas, ca = 1;
    memset(dp, -1, sizeof(dp));
    scanf("%d", &cas);
    while(cas--) {
        ll x;
        scanf("%lld", &x);
        printf("Case %d: %lld\n", ca++, gao(x));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值