题意不多赘述。
思路
看到 1 0 1000 10^{1000} 101000 这个数据范围,不是结论题,就是高精;不是高精,就是数位DP。
又看到求方案数,又要砝码数量最小,不出意外,就是数位DP了。
开始考虑DP。注意到砝码数量是 4 4 4 的幂,容易想到将原数转化为 4 4 4 进制数。
看到样例,容易发现,方案由一堆砝码相加和一堆砝码相减两种情况组成,这就意味着DP过程需要分类讨论。同时发现相减的本质是借位,因为对于一个 4 4 4 进制数 ( 003 ) 4 (003)_4 (003)4 可以由 ( 010 ) 4 − ( 001 ) 4 (010)_4-(001)_4 (010)4−(001)4 ,于是我们终于正式开始DP啦!
设两个结构体数组, f i f_i fi 和 g i g_i gi 分别用来记录不借位和借位的情况 ,结构体里有两个元素, a n s ans ans 和 s u m sum sum, 分别记录在这个 4 4 4 进制数下第 i i i 位的方案数和至少用的砝码的数量。
方程是非常恶心的分类讨论,但重载一下运算符就会很简单,但我没重载。
这里就简述一下方程思路,可以自己推,也可以去看代码。
方程思路:先上一位转移过来的 s u m sum sum,取 s u m sum sum 较小的 a n s ans ans 转移到这一位来,如果 s u m sum sum 相同,这一位的 a n s ans ans 就加上两种 a n s ans ans(配合代码食用效果更佳)。
代码
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9;
char s[1005];
int a[5005] , b[5005] , len , n;
struct node{
int ans , sum;
}f[5005] , g[5005];
signed main()
{
cin >> s + 1;
len = strlen(s + 1);
for(int i = 1;i <= len;i++)a[len - i + 1] = s[i] - '0';
while(len)
{
a[0] = 0;
for(int i = len;i;i--)a[i - 1] += (a[i] % 4) * 10 , a[i] /= 4;
b[++ n] = a[0] / 10;
while(len != 0 && a[len] == 0)len --;
}
n++;
f[0].ans = 1;f[0].sum = 0;
g[0].ans = 0;g[0].sum = 1e9;
for(int i = 1;i <= n;i++)
{
if(f[i - 1].sum + b[i] == g[i - 1].sum + b[i] + 1)f[i].ans = (f[i - 1].ans + g[i - 1].ans) % mod , f[i].sum = f[i - 1].sum + b[i];
if(f[i - 1].sum + b[i] < g[i - 1].sum + b[i] + 1) f[i].ans = (f[i - 1].ans) % mod , f[i].sum = f[i - 1].sum + b[i];
if(f[i - 1].sum + b[i] > g[i - 1].sum + b[i] + 1) f[i].ans = (g[i - 1].ans) % mod , f[i].sum = g[i - 1].sum + b[i] + 1;
if(f[i - 1].sum + 4 - b[i] == g[i - 1].sum + 3 - b[i])g[i].ans = (f[i - 1].ans + g[i - 1].ans) % mod , g[i].sum = f[i - 1].sum + 4 - b[i];
if(f[i - 1].sum + 4 - b[i] < g[i - 1].sum + 3 - b[i])g[i].ans = (f[i - 1].ans) % mod , g[i].sum = f[i - 1].sum + 4 - b[i];
if(f[i - 1].sum + 4 - b[i] > g[i - 1].sum + 3 - b[i])g[i].ans = (g[i - 1].ans) % mod , g[i].sum = g[i - 1].sum + 3 - b[i];
}
cout << f[n].ans;
return 0;
}