牛客多校:数位DP

题目地址::登录—专业IT笔试面试备考平台_牛客网

题目大致意思:给你三个数A、B、C,然后问你有多少对(x,y)(1<=x<=A,1<=y<=B)能够满足给定的两个条件其中的一个条件,(x&y)>C和(x^y)<C;

输入:
3
3 4 2
4 5 2
7 8 5
输出:
5
7
31

做法:典型的数位DP,而且数位DP只是一个比较规范的暴力,就是在DFS的过程中减少重复的运算,而且在数位DP中最重要的就是要枚举每一位的上边界。就这题来讲,每一位的上边界与前面的取值有关,如果前面取得值有小于当前的边界的话,那么后面的上边界都可以定义为1。首先我们需要处理一下每一位如果前面没有取小于边界的话各位的上边界。接下来我们可以先定义一个s来表示当前dfs的是第几位就是位数,然后为x和y分别定义一个ua和ub,为0的时候表示前面没有取的小于上边界的值,那么上边界就是uA了,如果取值是1的话,那么表示当前dfs的状态出现过小于上边界的值,那么上边界的取值就是1了。然后也需要定义一个yu的值,yu为-1的时候表示的是前面取得值&起来要小于C已经出现过的值。取值为0的时候表示的是前面取值&起来与C相等,为1的时候表示&起来的值大于C。而且又因为x和y的取值必须大于1所以我们还要加上两个状态za和zb,表示前面取值有没有取到过1。然后一直dfs到s==0的时候,表示最低位了,就返回za&zb&(yu>0||yh>0)表示的是,x和y大于1而且&起来大于C或者异或起来小于C。

#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
bool uA[32], uB[32], uC[32];
LL A, B, C;
LL dp[32][2][2][3][3][2][2];
//s为第几位,因为要枚举上边界,所以还需要两位ua和ub表示当前的上界,比如前面出现过比A还小的值,那么上边界就可以是1不然就是uA
//还要定义一个yu和yh,-1表示不满足,0表示等于,1表示已经满足了。
//又因为x和y的值必须大于1,所以还要给A和B都加上一个判断za和zb,判断是否出现过1
LL dfs(int s, int ua, int ub, int yu, int yh, int za, int zb) {
    LL &res = dp[s][ua][ub][yu+1][yh+1][za][zb];
//    不重复计算
    if (res != -1) return res;
//    表示已经到了最低位了
    if (s == 0) {
        return res = za&&zb&&(yu>0||yh>0);
    }
    s--; res = 0;
    for (int x = 0; x <= (ua==0?uA[s]:1); x++) {
        for (int y = 0; y <= (ub==0?uB[s]:1); y++) {
            res += dfs(s,ua|(x<uA[s]),ub|(y<uB[s]),yu==0?((x&y)-uC[s]):yu,yh==0?(uC[s]-(x^y)):yh,za|x,zb|y);
        }
    }
    return res;
}
int main() {
//    freopen("in.txt", "r", stdin);
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%lld%lld%lld", &A,&B,&C);
        for (int i = 31; i >= 0; i--) {
            LL tmp = (1ll<<i);
            uA[i] = (A&tmp);
            uB[i] = (B&tmp);
            uC[i] = (C&tmp);
        }
        memset(dp,-1,sizeof(dp));
        printf("%lld\n", dfs(31,0,0,0,0,0,0));
    }
    return 0;
}

总结:没有做过数位DP的题,所以在遇到这种题的时候,虽然明知道是数位DP但是就是不知道要怎么做。所以还需要多刷题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值