定义数的消除操作为选定[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
Input
单组测试数据。
三个整数L、R和k(1<=L<=R<=10^18,1<=k<=18)。
Output
一个整数表示答案。
Input示例
10 21 2
Output示例
9
思路:
消除一个数的话考虑按照数位的方式。这里需要用到0-9构成的单调递增栈。
1:比如数字123, 按数位看的话是一个单调递增栈,需要操作3次才能完全消除;
2:比如数字121,在最后一个1加入栈时,发现前面有2比它大,这时要将2消除,但因为之前第一位是1已经加入栈里,所以这个1就不再进栈。共需要2次操作可以完全消除。
3:比如数字221,在第二个2进栈时,发现前面有个2进栈,因此不需入栈,在最后一个1进栈时,将前面的2消除,然后1进栈。共需要2次操作可以完全消除。
因为数位只有0-9共十个数字,所以可以用十个二进制数来表示进栈出栈。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MAXN = 30;
typedef long long ll;
int a[MAXN];
ll L, R, k, dp[20][1 << 10][20];
int cal(int p, int i)
{
for (int j = i + 1; j <= 9; j++)
{
if (p & (1 << j))
{
p ^= (1 << j);
}
}
return p;
}
ll dfs(int pos, int p, int t, bool limit)
{
if (pos < 0)
{
return t == k;
}
if (!limit && dp[pos][p][t] != -1)
{
return dp[pos][p][t];
}
int up = limit? a[pos] : 9;
ll result = 0;
for (int i = 0; i <= up; i++)
{
if (p & (1 << i) || i == 0)
{
result += dfs(pos - 1, cal(p, i), t, limit && i == a[pos]);
}
else
{
result += dfs(pos - 1, cal(p | (1 << i), i), t + 1, limit && i == a[pos]);
}
}
if (!limit)
{
dp[pos][p][t] = result;
}
return result;
}
ll solve(ll x)
{
memset(dp, -1, sizeof(dp));
int pos = 0;
while (x)
{
a[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1, 0, 0, true);
}
int main()
{
cin >> L >> R >> k;
cout << solve(R) - solve(L - 1);
return 0;
}