『HDU 5787』K-wolf Number

转载声明:http://blog.csdn.net/chy20142109/article/details/52103601
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5787

题意:
给你一个区间的【L,R】的数字,然后给你一个K,求要使连续K位都不会重复的数字的个数.

这里写图片描述
例如这样就是一个符合条件的数.

个人感想:
我感觉我已经很久没做过数位DP的题了.我做这题的时候居然一点感觉都没.好吧.具体我是参照转载(虽然什么都没看懂,我直接硬刚了一下就明白了)

dp[pos][p1][p2][p3][p4]代表:当前数位pos下,前4个数分别是p4,p3,p2,p1.其中pi=10代表前面没有数字
这里写图片描述

首先代码的想法是枚举每一个数位, 假设当前我现在枚举【50-12345】,那么就可以通过
【0-12345】符合的个数-【0-50】符合的个数 就是我们要的答案
口口口口口 首先我们检测每一位,用最基础的数位dp思想,
0 口口口口 第一个位置填写前导0.
0 0 口口口 同样.
0 0 0 口口
0 0 0 0 口
**0 0 0 0 0 **
**0 0 0 0 1 **.
**0 0 0 0 2 **

**1 2 3 4 5 **
就是一种暴力的想法,这样肯定超时,我们可以通过记忆化,因为如果我们只有有些数位是重复计算的,我们记录 它之前的4位,我们就可以知道后面有多少个数符合了.这样就不麻烦了. 用这个想法去看看代码,就很容易懂.

我做的时候会有一个疑问在flag中,flag有什么用呢?flag用来指示当前这个数是不是开始需要枚举. 可能说的太恶心了… 就是 flag?dig[pos]:9; 如果flag=true.我只能枚举属于它的正常范围,例如pos=1,那么 12345,的万位 1,只能从**【0->1】开始枚举.就是这样意思, 记忆化必须在当前不是flag=false**的时候记忆.

嗯差不多了,再看看代码去意会一下应该就明白了.

分析:数位DP

代码:

/* Author:GavinjouElephant
 * Title:
 * Number:
 * main meanning:
 *
 *
 *
 */

//#define OUT
#include <iostream>
using namespace std;
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <sstream>
#include <cctype>
#include <vector>
#include <set>
#include <cstdlib>
#include <map>
#include <queue>
//#include<initializer_list>
//#include <windows.h>
//#include <fstream>
//#include <conio.h>
#define MaxN 0x7fffffff
#define MinN -0x7fffffff
#define Clear(x) memset(x,0,sizeof(x))
typedef long long ll;
const int INF=0x3f3f3f3f;
ll L,R;
ll K;
ll top;
ll dig[20];
ll dp[20][11][11][11][11];
bool check(ll p1,ll p2,ll p3,ll p4,ll now)
{
    if(K==2) return now!=p4;
    if(K==3) return now!=p4&&now!=p3;
    if(K==4) return now!=p4&&now!=p3&&now!=p2;
    return          now!=p4&&now!=p3&&now!=p2&&now!=p1;
}
ll dfs(ll pos,ll p1,ll p2,ll p3,ll p4,bool flag)
{
    if(pos==0) return p4!=10;//前面的第一个数已经确定了,那么就不需要枚举了.
    if(!flag&&dp[pos][p1][p2][p3][p4]!=-1) return dp[pos][p1][p2][p3][p4];

    ll Ed=flag?dig[pos]:9;
    ll ans=0;

    for(ll i=0;i<=Ed;i++)
    {
        if( i==0&& p4==10 )
            ans+=dfs(pos-1,10,10,10,10,flag&&i==Ed);
        else if( check(p1,p2,p3,p4,i) )
            ans+=dfs(pos-1,p2,p3,p4,i,flag&&i==Ed);
    }
    if(!flag) dp[pos][p1][p2][p3][p4]=ans;
    return ans;
}
ll solve(ll n)
{
    if(n<=0)return 0;
    top=0;
    while(n)
    {
        dig[++top]=n%10;
        n/=10;
    }
    return dfs(top,10,10,10,10,true);
}
int main()
{
#ifdef OUT
    freopen("coco.txt","r",stdin);
    freopen("lala.txt","w",stdout);
#endif
   while(scanf("%I64d %I64d %I64d",&L,&R,&K)!=EOF)
   {
       memset(dp,-1,sizeof(dp));
       printf("%I64d\n",solve(R)-solve(L-1));
   }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值