数位dp

1. 不要62(数位dp)

题目链接
在这里插入图片描述在这里插入图片描述

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
typedef long long ll;
int a[20];
int dp[20][2];
int dfs(int pos, int pre, int sta, bool limit) {
    //limit是表示前一位是否为上限位
    //pos表示当前位
    //pre表示前一位
    //sta表示前一位是否为题目限制敏感数(这题是6)
    if (pos == -1)
        return 1;
    if (!limit && dp[pos][sta] != -1)
        return dp[pos][sta];
    int up = limit ? a[pos] : 9;
    int tmp = 0;
    for (int i = 0; i <= up; i++) {
        if (pre == 6 && i == 2)
            continue;
        if (i == 4)
            continue;  //都是保证枚举合法性
        tmp += dfs(pos - 1, i, i == 6, limit && i == a[pos]);
    }
    if (!limit)
        dp[pos][sta] = tmp;
    return tmp;
}
int solve(int x) {
    int pos = 0;
    while (x) {
        a[pos++] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1, -1, 0, true);
}
int main() {
    int le, ri;
    // memset(dp,-1,sizeof dp);可优化
    while (~scanf("%d%d", &le, &ri) && le + ri) {
        memset(dp, -1, sizeof dp);
        printf("%d\n", solve(ri) - solve(le - 1));
    }
    return 0;
}

2.F(x)(数位dp减法)

题目链接
在这里插入图片描述

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>

using namespace std;
const int N = 1e4 + 5;
int dp[12][N];
int f(int x) {
    if (x == 0)
        return 0;
    int ans = f(x / 10);
    return ans * 2 + (x % 10);
}
int all;
int a[12];
int dfs(int pos, int sum, bool limit) {
    if (pos == -1) {
        return sum <= all;
    }
    if (sum > all)
        return 0;
    if (!limit && dp[pos][all - sum] != -1)
        return dp[pos][all - sum];
    int up = limit ? a[pos] : 9;
    int ans = 0;
    for (int i = 0; i <= up; i++) {
        ans += dfs(pos - 1, sum + i * (1 << pos), limit && i == a[pos]);
    }
    if (!limit)
        dp[pos][all - sum] = ans;
    return ans;
}
int solve(int x) {
    int pos = 0;
    while (x) {
        a[pos++] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1, 0, true);
}
int main() {
    int a, ri;
    int T_T;
    int kase = 1;
    scanf("%d", &T_T);
    memset(dp, -1, sizeof dp);
    while (T_T--) {
        scanf("%d%d", &a, &ri);
        all = f(a);
        printf("Case #%d: %d\n", kase++, solve(ri));
    }
    return 0;
}

3.Round Numbers

题目链接
在这里插入图片描述题解:题解链接

4.Beautiful Numbers

题目链接
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int t;
int a[20];
ll dp[20][110][110][110];
ll dfs(int pos, int mod, int yu, int sum, bool limit) {
    if (sum > mod) {
        return 0;
    }
    if (pos == -1) {
        return mod == sum && yu == 0;
    }

    if (!limit && dp[pos][mod][yu][sum] != -1) {
        return dp[pos][mod][yu][sum];
    }
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; ++i) {
        ans +=
            dfs(pos - 1, mod, (yu * 10 + i) % mod, sum + i, limit && (i == up));
    }
    if (!limit) {
        dp[pos][mod][yu][sum] = ans;
    }
    return ans;
}
ll solve(ll n) {
    int pos = 0;
    while (n) {
        a[pos++] = n % 10;
        n /= 10;
    }
    ll ans = 0;
    for (int i = 1; i <= 108; ++i) {
        ans += dfs(pos - 1, i, 0, 0, true);
    }
    return ans;
}
int main() {
    cin >> t;
    memset(dp, -1, sizeof(dp));
    for (int i = 1; i <= t; ++i) {
        ll n;
        cin >> n;
        cout << "Case " << i << ": " << solve(n) << endl;
    }

    return 0;
}

题解:数位dp,题解链接。有一步要知道的是(yu * 10 + i) % mod,就是假如1234%3 = ((((1*10+2)%3)10+3)%310+4)%3,它们的结果是一样的

5.明七暗七

题目链接
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n, m;
int a[20];
ll dp[20][20][2];
ll dfs(int pos, int sum, int sta, bool limit) {
    if (pos == -1) {
        return !sum || sta;
    }
    if (!limit && dp[pos][sum][sta] != -1) {
        return dp[pos][sum][sta];
    }
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; ++i) {
        ans += dfs(pos - 1, ((sum * 10) + i) % 7, sta || i == 7,
                   limit && (i == up));
    }
    if (!limit) {
        dp[pos][sum][sta] = ans;
    }
    return ans;
}
ll solve(ll x) {
    int pos = 0;
    
    while (x) {
        a[pos++] = x % 10;
        x /= 10;
    }

    return dfs(pos - 1, 0, 0, true);
}
int main() {
    cin >> m >> n;
    memset(dp, -1, sizeof(dp));
    ll l = m + 1, r = 1e18, mid = l + r >> 1;
    ll sub = solve(m);
    while (l < r) {
        if (solve(mid) - sub >= n)
            r = mid;
        else
            l = mid + 1;
        mid = l + r >> 1;
    }
    printf("%lld\n", mid);
    return 0;
}

题解:有点像是“不要62”的板子,但是又有点不一样,不一样在于这里一当有一个7都要记录在sta中,相当于sta || i == 7,因为前面数位一但有了7,就会影响后面所有数位的判断,导致dp数组要即使是相同的pos和sum也要分别对sta=0和sta=1区分。

**

6.好朋友

**
题目链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll l, r, t, k;
int a[20];
ll dp[20][2][2][2][2];
ll dfs(int pos, bool limit, bool iffirst, bool sta1, bool sta2, bool sta3) {
    if (pos == -1) {
        return sta3;
    }
    if (!limit && dp[pos][iffirst][sta1][sta2][sta3] != -1) {
        return dp[pos][iffirst][sta1][sta2][sta3];
    }
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; ++i) {
        ans += dfs(pos - 1, limit && i == up, iffirst && i == 0,
                   sta1 || (!iffirst && i == 0),
                   sta2 || (!iffirst && sta1 && i == 0),
                   sta3 || (!iffirst && sta2 && i == 7));
    }
    if (!limit) {
        dp[pos][iffirst][sta1][sta2][sta3] = ans;
    }
    return ans;
}
ll solve(ll x) {
    int pos = 0;
    while (x) {
        a[pos++] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1, true, true, false, false, false);
}
int main() {
    cin >> t;
    memset(dp, -1, sizeof(dp));
    for (int i = 0; i < t; ++i) {
        scanf("%lld %lld", &l, &r);
        k ^= (solve(r) - solve(l - 1));
    }
    cout << k << endl;
    return 0;
}

题解:这类题写多了有点思路了,就是把当前位的所有状态都记录下来,做一个dp数组,但凡少一个状态都可能影响dp数组的计数,例如这题的iffirst判断是否为前导零,sta1判断是否为第一个零,sta2判断是否为第二个零,sta3判断是否为7,于是每个状态都开一维储存,然后按模板搞就行了。

**

7.[AHOI2009]SELF 同类分布

**
题目链接
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll l, r;
int a[20];
ll dp[20][200][200];
ll dfs(int pos, bool limit, int mod, int sum, int all) {
    if (sum > all) {
        return 0;
    }
    if (pos == -1) {
        return sum == all && mod == 0;
    }
    
    if (!limit && dp[pos][mod][sum] != -1) {
        return dp[pos][mod][sum];
    }
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; ++i) {
        ans +=
            dfs(pos - 1, limit && i == up, (mod * 10 + i) % all, sum + i, all);
    }
    if (!limit) {
        dp[pos][mod][sum] = ans;
    }
    return ans;
}
ll solve(ll x) {
    int pos = 0;
    while (x) {
        a[pos++] = x % 10;
        x /= 10;
    }
    ll ans = 0;
    for (int i = 1; i <= 168; ++i){
        memset(dp, -1, sizeof(dp));
        ans += dfs(pos - 1, true, 0, 0, i);
    }
        
    return ans;
}
int main() {
    memset(dp, -1, sizeof(dp));
    scanf("%lld %lld", &l, &r);
    cout << (solve(r) - solve(l - 1)) << endl;
    return 0;
}

题解:因为题目数据超级大,有10^18,所以限制了正常的做法,不能开longlong的太多维数组,不然会爆内存,于是就减少了本应该储存all的这一维,但省略了all这一维之后又产生了新的bug,就是对于不同的all值如果仍然使用相同的dp数组就会导致一些意料以外的冲突,所以每次做完dfs都应该恢复dp数组的默认值-1重新计数。

**

8.[CQOI2013]二进制A+B(难,不是很理解,不知道算不算数位dp)

**
题目链接
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <typename T>
inline T min(T& a, T& b) {
    return a < b ? a : b;
}
template <typename T>
inline int find(T a) {
    int res = 0;
    for (; a; a >>= 1)
        ++res;
    return res;
}  //计算位数
template <typename T>
inline int cont(T x) {
    int res = 0;
    for (; x; x &= (x - 1))
        ++res;
    return res;
}  //计算二进制下1的个数
ll f[33][33][33][33][2], ans, inf;
int main() {
    memset(f, 63, sizeof(f));
    inf = ans = f[0][0][0][0][0];
    int A, B, C, mxd;
    scanf("%d%d%d", &A, &B, &C);
    mxd = max(max(find(A), find(B)), find(C));
    A = cont(A), B = cont(B), C = cont(C);
    f[0][0][0][0][0] = 0;
    for (int i = 0; i <= mxd; ++i)
        for (int a = 0; a <= min(i + 1, A); ++a)
            for (int b = 0; b <= min(i + 1, B); ++b)
                for (int c = 0; c <= min(i + 1, C); ++c) {  
						// 8种枚举,其实都是min左边的状态和右边的状态是一样的数位,
						//都是从高位到低位,右边是低位全置零的状态,左边是低位置零或置一的八种状态
						//而且每次左边的c+1,右边都要同等地|1,其实就是+1,两者总是同步进行的
						//只要a或者b或者是进位上进了一,c就要+1,当a,b,进位三者中有两者同为一,就可以抵消然后进位,c就不用+1了
                    
                    f[i + 1][a][b][c][0] = min(f[i + 1][a][b][c][0], f[i][a][b][c][0] << 1);
                    
                    f[i + 1][a + 1][b][c + 1][0] = min(f[i + 1][a + 1][b][c + 1][0], f[i][a][b][c][0] << 1 | 1);
                    
                    f[i + 1][a][b + 1][c + 1][0] = min(f[i + 1][a][b + 1][c + 1][0], f[i][a][b][c][0] << 1 | 1);
                    
                    f[i + 1][a + 1][b + 1][c][0] = min(f[i + 1][a + 1][b + 1][c][0], f[i][a][b][c][1] << 1);

                    
                    f[i + 1][a][b][c + 1][1] = min(f[i + 1][a][b][c + 1][1],f[i][a][b][c][0] << 1 | 1);
                    
                    f[i + 1][a][b + 1][c][1] =min(f[i + 1][a][b + 1][c][1], f[i][a][b][c][1] << 1);
                    
                    f[i + 1][a + 1][b][c][1] =min(f[i + 1][a + 1][b][c][1], f[i][a][b][c][1] << 1);
                    
                    f[i + 1][a + 1][b + 1][c + 1][1] =min(f[i + 1][a + 1][b + 1][c + 1][1],f[i][a][b][c][1] << 1 | 1);
                }
    for (int i = 0; i <= mxd; ++i)
        ans = min(ans, f[i][A][B][C][0]);
    if (ans == inf)
        printf("-1");
    else
        printf("%lld", ans);
    return 0;
}

题解:挺难理解的,我也不是很理解,代码里面有一点我的注释理解,以后有了新的理解会更新。

**

9.[CQOI2016]手机号码

**
题目链接
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll l, r;
int a[20];
ll dp[15][20][20][20][20];
ll dfs(int pos, bool limit, int used, int last, int sum, int maxl, int lead) {
    if (pos == -1) {
        return maxl >= 3;
    }
    if (!limit && dp[pos][used][last][sum][maxl] != -1) {
        return dp[pos][used][last][sum][maxl];
    }
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for (int i = lead; i <= up; ++i) {
        if ((used == 4 && i == 8) || (used == 8 && i == 4)) {
            continue;
        }
        ans += dfs(pos - 1, limit && i == up, i == 8 || i == 4 ? i : used, i,
                   i == last ? sum + 1 : 1, max(maxl, sum + (i == last)), 0);
    }
    if (!limit) {
        dp[pos][used][last][sum][maxl] = ans;
    }
    return ans;
}
ll solve(ll n) {
    int pos = 0;
    while (n) {
        a[pos++] = n % 10;
        n /= 10;
    }
    if (pos != 11) {
        return 0;
    }
    return dfs(pos - 1, true, 0, 0, 0, 0, 1);
}
int main() {
    memset(dp, -1, sizeof(dp));
    cin >> l >> r;
    cout << solve(r) - solve(l - 1) << endl;
    return 0;
}```
题解:注意题目说电话号码只能是11位,不是11位的直接返回0。lead的作用的可以规避前导零,让dfs从最高位开始的话只能最小是1。其他的老生常谈。
**

## 10.[SCOI2009]WINDY数

**
[题目链接](https://ac.nowcoder.com/acm/problem/20268)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210506163717442.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JfYl9sYWlfbGFp,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210506163725908.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JfYl9sYWlfbGFp,size_16,color_FFFFFF,t_70)

```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll l, r;
int a[20];
ll dp[15][20][2];
ll dfs(int pos, bool limit, int last, bool zero) {
    if (pos == -1) {
        return 1;
    }
    if (!limit && dp[pos][last][zero] != -1) {
        return dp[pos][last][zero];
    }
    int up = limit ? a[pos] : 9;
    ll ans = 0;
    for (int i = 0; i <= up; ++i) {
        if (!zero && last != -1 && abs(i - last) < 2) {
            continue;
        }
        ans += dfs(pos - 1, limit && i == up, i, zero && i == 0);
    }
    if (!limit) {
        dp[pos][last][zero] = ans;
    }
    return ans;
}
ll solve(ll n) {
    int pos = 0;
    while (n) {
        a[pos++] = n % 10;
        n /= 10;
    }
    return dfs(pos - 1, true, -1, true);
}
int main() {
    cin >> l >> r;
    memset(dp, -1, sizeof(dp));
    cout << solve(r) - solve(l - 1) << endl;

    return 0;
}

题解:还是老生常谈了,注意区分前导零。

weixin063传染病防控宣传微信小程序系统的设计与实现+springboot后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值