题意不多赘述。
思路
看到 10100010^{1000}101000 这个数据范围,不是结论题,就是高精;不是高精,就是数位DP。
又看到求方案数,又要砝码数量最小,不出意外,就是数位DP了。
开始考虑DP。注意到砝码数量是 444 的幂,容易想到将原数转化为 444 进制数。
看到样例,容易发现,方案由一堆砝码相加和一堆砝码相减两种情况组成,这就意味着DP过程需要分类讨论。同时发现相减的本质是借位,因为对于一个 444 进制数 (003)4(003)_4(003)4 可以由 (010)4−(001)4(010)_4-(001)_4(010)4−(001)4 ,于是我们终于正式开始DP啦!
设两个结构体数组,fif_ifi 和 gig_igi 分别用来记录不借位和借位的情况 ,结构体里有两个元素, ansansans 和 sumsumsum, 分别记录在这个 444 进制数下第 iii 位的方案数和至少用的砝码的数量。
方程是非常恶心的分类讨论,但重载一下运算符就会很简单,但我没重载。
这里就简述一下方程思路,可以自己推,也可以去看代码。
方程思路:先上一位转移过来的 sumsumsum,取 sumsumsum 较小的 ansansans 转移到这一位来,如果 sumsumsum 相同,这一位的 ansansans 就加上两种 ansansans(配合代码食用效果更佳)。
代码
#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;
}