【数位DP】P3464

题意不多赘述。

思路

看到 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;
 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值