HDU 5787 K-wolf Number (数位DP)

6 篇文章 0 订阅

此为转载,原帖:http://blog.csdn.net/chy20142109/article/details/52103601

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5787

题意:求区间[L,R]内,任意相邻k位(如果位数不足k,就是全部的数位)没有两两相同的数位的数的个数。

思路:数位DP,因为K<=5,我们最多需要保存下来当前位的前4位就足够了。
因为dp[pos][p1][p2][p3][p4]表示,现在枚举取第pos位,pos位之前的四位分别为p1,p2,p3,p4,p4是pos的上一位。那么p1~p4的范围就是0~9,但是数是没有前导0的,而且k可能为2,3,4,不需要保存到前4位,所以我们令p = 10来表示这一位不需要保存,也许可能是前导0,也许是不需要保存。

那么dfs函数可以写成 dfs( int pos , int p1 , int p2 , int p3 , int p4 , bool flag ) flag表示pos位能否取到全部的数位(0~9)还是会受到前面最高位的影响只能取一部分。
那么统计下一位的时候就可以分为两种情况:1、pos位之前都取的0,而pos位也取0,一直都是前导零。2、pos位取的i去和p判断一下有没有重复(根据k来判断需要比较几个p),假设这种情况,K = 3,而我们现在正在取第二位,i会和p4和p3去比较一下,但是只有p4是我们取过的值,不过p3此时是10,所以也是可以向下统计的。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define pb push_back
#define mp make_pair
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define calm (l+r)>>1
const int INF = 2139062143;

ll dp[20][11][11][11][11];
int digit[20];
ll L,R;
int K;
bool check(int x,int a,int b,int c,int d){//根据K来判断需要和几个数做比较
    if(K==2)return x!=a;
    if(K==3)return x!=a&&x!=b;
    if(K==4)return x!=a&&x!=b&&x!=c;
    if(K==5)return x!=a&&x!=b&&x!=c&&x!=d;
}
ll dfs(int pos,int p1,int p2,int p3,int p4,bool flag){
    if(pos==0){//如果p4==10,表示之前全部取了0
        return p4!=10;
    }
    if(!flag&&dp[pos][p1][p2][p3][p4]!=-1)return dp[pos][p1][p2][p3][p4];//如果保存了结果就直接返回
    int mx=9;
    if(flag)mx=digit[pos];//如果!flag,表示当前位置一定比原来的数小,那么[0,9]都可以取,否则,只能取到当前位置的数
    ll ans=0;
    for(int i=0;i<=mx;i++){
        if(i==0&&p4==10)ans+=dfs(pos-1,10,10,10,10,flag&&i==mx);
        else if(check(i,p4,p3,p2,p1)){
            ans+=dfs(pos-1,p2,p3,p4,i,flag&&i==mx);
        }
    }
    if(!flag)dp[pos][p1][p2][p3][p4]=ans;
    return ans;
}
ll cal(ll x){
    if(x==0)return 0;
    int len=0;
    while(x){
        digit[++len]=x%10;
        x/=10;
    }
    return dfs(len,10,10,10,10,true);
}
int main(){
    //freopen("D://input.txt","r",stdin);
    while(scanf("%I64d%I64d%d",&L,&R,&K)!=EOF){
        memset(dp,-1,sizeof dp);
        printf("%I64d\n",cal(R)-cal(L-1));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值