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; }