【数位DP-三维dp数组】HDU 3652 B-number

 HDU 3652 B-number

题意:给一个数n,求区间[1, n]中具有连续13,并且可以可以被13整除的数的个数。

思路:对于一个数来说,它有两个固有性质。1:是不是13的倍数. 2:有没有连续数位为13. 我们的dp数组如何来表示出所谓无后效性就很重要。我们可以用一维来表示是不是满足性质2,再用一维表示它mod13的余数。那么dp也就是一个三维数组。

实践证明两维真的不可。第一道三维数位DP,还是有、东西。追根到底还是我太菜了(大哭)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <limits>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e4 + 5;

int n;
int a[35];
int dp[35][15][3];

int dfs(int pos, int re, int sta, bool limit)
{
    if(pos == -1)
        return re == 0 && sta == 2;
    if(!limit && dp[pos][re][sta] != -1)
        return dp[pos][re][sta];
    int up = limit ? a[pos] : 9;
    int ans = 0;
    for(int i = 0; i <= up; i ++ )
    {
        int nextSta = 0;
        if(sta == 2)
            nextSta = 2;
        else if(sta == 1 && i == 3)
            nextSta = 2;
        else if(i == 1)
            nextSta = 1;
        ans += dfs(pos - 1, (re * 10 + i) % 13, nextSta, limit && i == a[pos]);
    }
    if(!limit)
        dp[pos][re][sta] = ans;
    return ans;
}

int solve(int x)
{
    int pos = 0;
    while(x)
    {
        a[pos ++ ] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1, 0, 0, true);
}
int main()
{
    memset(dp, -1, sizeof(dp));
    while(~scanf("%d", &n))
    {
        printf("%d\n", solve(n));
    }
    return 0;
}

下面这个是T了的代码。最开始dp开的是二维数组,第一维表示pos,第二维表示sta(截至当前pos为止是不是满足连续13的条件,如果没有满足就是2,如果前一位是1就是1,否则就是0)。

但是这个有一个问题,就是会造成混淆。因为我们只记录了pos和sta,没有考虑这个数它mod13的余数。但是相同pos和相同sta的数可能有很多 ,但是它们的dp值并不应该相同,因为它们mod13的值不同。所以这样的话结果会偏大。

而如果我们只记录余数为0的dp值,那么就要重复计算很多个数据,于是就爆了,没有疑问T了。qaq

但是这也让我明白了,如果dp数组不是记录数字而是记录状态的话,那么一定要考虑一个数字对于相应题目来说它所有种的状态。比如这个题,某个数x,他的状态有两种,第一个是是否有连续13,再一个就是是不是可以被13整除。但是不是13倍数的数,相应的dp值可能还是不同,所以我们应该记录的是x%13这个量.

定义状态的方法是多种多样的,满足无后效性复杂度可以接受即可。但一般来说,为避免重复计算,应该尽可能记下有影响的量(除非你确定其他的量可以替代它)。【这是讲题学长说的哈哈哈orz】
 

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <limits>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 1e4 + 5;

int n;
int a[35];
int dp[35][3];

int dfs(int pos, int re, int sta, bool limit)//re是目前为止%13的余数,sta是上一位是1,sta = 1
{
    if(pos == -1)
        return re == 0 && sta == 2;
    if(!limit && !re && dp[pos][sta] != -1)
        return dp[pos][sta];
    int up = limit ? a[pos] : 9;
    int ans = 0;
    for(int i = 0; i <= up; i ++ )
    {
        int nextSta = 0;
        if(sta == 1 && i== 3)
            nextSta = 2;
        else if(sta == 2)
            nextSta = 2;
        else if(i == 1)
            nextSta = 1;
        ans += dfs(pos - 1, (re * 10 + i) % 13, nextSta, limit && i == a[pos]);
    }
    if(!limit && !re)
        dp[pos][sta] = ans;
    return ans;
}

int solve(int x)
{
    int pos = 0;
    while(x)
    {
        a[pos ++ ] = x % 10;
        x /= 10;
    }
    return dfs(pos - 1, 0, 0, true);
}
int main()
{
    memset(dp, -1, sizeof(dp));
    while(~scanf("%d", &n))
    {
        printf("%d\n", solve(n));
    }
    return 0;
}
发布了180 篇原创文章 · 获赞 54 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/103886842