数位DP 神模板 详解

为了方便自己参看,我把代码复制过来吧

//    pos    = 当前处理的位置(一般从高位到低位)
//    pre    = 上一个位的数字(更高的那一位)
//    status = 要达到的状态,如果为1则可以认为找到了答案,到时候用来返回,
//            给计数器+1。
//    limit  = 是否受限,也即当前处理这位能否随便取值。如567,当前处理6这位,
//            如果前面取的是4,则当前这位可以取0-9。如果前面取的5,那么当前
//            这位就不能随便取,不然会超出这个数的范围,所以如果前面取5的
//            话此时的limit=1,也就是说当前只可以取0-6。
//
//    用DP数组保存这三个状态是因为往后转移的时候会遇到很多重复的情况。
int    dfs(int pos,int pre,int status,int limit)
{
    //已结搜到尽头,返回"是否找到了答案"这个状态。
    if(pos < 1)
        return    status;

    //DP里保存的是完整的,也即不受限的答案,所以如果满足的话,可以直接返回。
    if(!limit && DP[pos][pre][status] != -1)
        return    DP[pos][pre][status];

    int    end = limit ? DIG[pos] : 9;
    int    ret = 0;
    
    //往下搜的状态表示的很巧妙,status用||是因为如果前面找到了答案那么后面
    //还有没有答案都无所谓了。而limti用&&是因为只有前面受限、当前受限才能
    //推出下一步也受限,比如567,如果是46X的情况,虽然6已经到尽头,但是后面的
    //个位仍然可以随便取,因为百位没受限,所以如果个位要受限,那么前面必须是56。
    //
    //这里用"不要49"一题来做例子。
    for(int i = 0;i <= end;i ++)
        ret += dfs(pos - 1,i,status || (pre == 4 && i == 9),limit && (i == end));

    //DP里保存完整的、取到尽头的数据
    if(!limit)
        DP[pos][pre][status] = ret;

    return    ret;
}

 

传送门 hdu 5787 K-wolf Number

吐槽:我以前是写过数位DP的,但是也就停留在表面上的理解,所以虽然我一看到题目就知道算法,但是,诶,没弄出来。。。感觉自己好渣!!!

题意:给你一个区间,让你找满足任意k个数位内都没有相同的数字的个数

思路:这题思路比较一致,dp[pos][a][b][c][d] pos表示当前位,a,b,c,d分别表示表示前4,3,2,1位。这里要考虑前导0的情况,d=10的时候表示前一位为0.所以当(d==10 && i==0)时表示当前的这位为0并且前四位都为0.所以向下dfs的话 ans+=dfs(temp-1,a,b,c,d,limit&&(i==ed));

/**************************************************************
    Problem:hdu 5787 K-wolf Number
    User: youmi
    Language: C++
    Result: Accepted
    Time:265MS
    Memory:3872K
****************************************************************/
//#pragma comment(linker, "/STACK:1024000000,1024000000")
//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <stack>
#include <set>
#include <sstream>
#include <cmath>
#include <queue>
#include <deque>
#include <string>
#include <vector>
#define zeros(a) memset(a,0,sizeof(a))
#define ones(a) memset(a,-1,sizeof(a))
#define sc(a) scanf("%d",&a)
#define sc2(a,b) scanf("%d%d",&a,&b)
#define sc3(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define scs(a) scanf("%s",a)
#define sclld(a) scanf("%I64d",&a)
#define pt(a) printf("%d\n",a)
#define ptlld(a) printf("%I64d\n",a)
#define rep(i,from,to) for(int i=from;i<=to;i++)
#define irep(i,to,from) for(int i=to;i>=from;i--)
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define lson (step<<1)
#define rson (lson+1)
#define eps 1e-6
#define oo 0x3fffffff
#define TEST cout<<"*************************"<<endl
const double pi=4*atan(1.0);

using namespace std;
typedef long long ll;
template <class T> inline void read(T &n)
{
    char c; int flag = 1;
    for (c = getchar(); !(c >= '0' && c <= '9' || c == '-'); c = getchar()); if (c == '-') flag = -1, n = 0; else n = c - '0';
    for (c = getchar(); c >= '0' && c <= '9'; c = getchar()) n = n * 10 + c - '0'; n *= flag;
}
int Pow(int base, ll n, int mo)
{
    if (n == 0) return 1;
    if (n == 1) return base % mo;
    int tmp = Pow(base, n >> 1, mo);
    tmp = (ll)tmp * tmp % mo;
    if (n & 1) tmp = (ll)tmp * base % mo;
    return tmp;
}
//***************************

ll k;
ll dp[20][11][11][11][11];
int dt[20];
int tt;
bool check(int temp,int a,int b,int c,int d)
{
    if(k==2)
    {
        if(temp==d)
            return true;
    }
    else if(k==3)
    {
        if(temp==d||temp==c)
            return true;
    }
    else if(k==4)
    {
        if(temp==d||temp==c||temp==b)
            return true;
    }
    else if(k==5)
    {
        if(temp==d||temp==c||temp==b||temp==a)
            return true;
    }
    return false;
}
ll dfs(int temp,int a,int b,int c,int d,int limit)
{
    if(temp<0)
        return 1ll;
    ll& now=dp[temp][a][b][c][d];
    if(!limit&&now)
        return now;
    ll ans=0;
    int ed=limit?dt[temp]:9;
    for(int i=0;i<=ed;i++)
    {
        if(check(i,a,b,c,d))
            continue;
        if(i==0&&d==10)
            ans+=dfs(temp-1,a,b,c,d,limit&&(i==ed));
        else
            ans+=dfs(temp-1,b,c,d,i,limit&&(i==ed));

    }
    if(!limit)
        now=ans;
    return ans;
}
ll sovle(ll n)
{
    tt=0;
    while(n)
    {
        dt[tt++]=n%10;
        n/=10;
    }
    zeros(dp);
    return dfs(tt-1,10,10,10,10,1);
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    ll l,r;
    while(~scanf("%I64d%I64d%I64d",&l,&r,&k))
    {
        printf("%I64d\n",sovle(r)-sovle(l-1));
    }
}
View Code

 

posted on 2016-08-03 22:52  中子星  阅读(466)  评论(0编辑  收藏  举报