51NOD 1623 完美消除 数位DP

题目描述:

  定义数的消除操作为选定[L,R,x],如果数的第L到第R位上的数字都大于等于x,并且这些数都相等,那么该操作是合法的(从低位到高位编号,个位是第一位,百位是第二位……),然后将这些位数上的数减x;否则就是不合法的,不能进行操作。对一个数操作最少的次数使得这个数变成0,这个操作次数称为该数的最小操作数。如:1232的最小操作数为3,一个合法解是[2,2,1],[1,3,2],[4,4,1]。

求L~R中最小操作数为k的数的个数。

 

例如:132,需要操作3次才能变为0。而131131 => 111131 => 111111 =>0

输入:

  单组测试数据。三个整数L、R和k(1<=L<=R<=10^18,1<=k<=18)

 

题解:

  典型数位DP

  设定dp[i][j][k] 前i位下所用数字状态j花费次数时k的个数

  注意这里的状态j是指 后面的放入数字能有重复效应的 状态

  例如 313 花费的次数是3 但131花费次数2    两者在第二位的状态 是(3)和(1)

  最后还要注意0不花费

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 30, M = 2e6+10, inf = 2e9, mod = 1e9+7;
int d[N];
long long L,R,k,dp[20][1<<10][20],vis[20][1<<10][20];
int cal(int p,int i) {
    for(int k=i+1;k<=9;k++) if(p&(1<<k)) p^=(1<<k);
    return p;
}
long long dfs(int dep,int f,int p,int K)
{
    if(dep<0) return K==k;
    if(f&&vis[dep][p][K]) return dp[dep][p][K];
    if(f){
        long long& ret = dp[dep][p][K];
        vis[dep][p][K]=1;
        for(int i=0;i<=9;i++)
        {
            if(p&(1<<i)||i==0) {
                ret+=dfs(dep-1,f,cal(p,i),K);
            }
            else ret+=dfs(dep-1,f,cal(p|(1<<i),i),K+1);
        }
        return ret;
    }
    else {
        long long ret = 0;
        for(int i=0;i<=d[dep];i++)
        {
            if(p&(1<<i)||i==0)
            ret+=dfs(dep-1,i<d[dep],cal(p,i),K);
            else ret+=dfs(dep-1,i<d[dep],cal(p|(1<<i),i),K+1);
        }
        return ret;
    }
}
long long solve(long long x)
{
    memset(dp,0,sizeof(dp));
    memset(vis,0,sizeof(vis));
    int len = 0;
    while(x){
        d[len++] = x%10;
        x/=10;
    }
    dfs(len-1,0,0,0);
}
int main(){
 while(scanf("%lld%lld%lld",&L,&R,&k)!=EOF)
   {
        printf("%lld\n",solve(R)-solve(L-1));
   }
    return 0;
}

 

posted @ 2016-07-10 20:08  meekyan  阅读(474)  评论(0编辑  收藏  举报