2020牛客暑期多校训练营第六场Harmony Pairs

一道关于计算和谐对(A, B)数量的数位动态规划题目,其中和谐对定义为S(A) > S(B),对于给定的整数N,要求找到所有满足条件的和谐对并模10^9+7。通过优化状态转移,将三维数组压缩为二维以适应大范围的N值,从而实现解题。" 111261585,8554531,GitHub Universe 2020:暗黑模式与全新体验,"['GitHub', '开发工具', '持续集成', '代码管理', '移动开发', '安全性']
摘要由CSDN通过智能技术生成

Harmony Pairs

原题请看这里

题目描述:

S ( x ) S(x) S(x)表示十进制表示下 x x x的每位数字之和,当 S ( A ) S(A) S(A) > > > S ( B ) S(B) S(B) ( A , B ) (A,B) (A,B)表示一个和谐对。
给定一个数 N N N,求满足 0 ≤ A ≤ B ≤ N 0≤A≤B≤N 0ABN的和谐对 ( A , B ) (A,B) (A,B)的数量,答案对 1 0 9 + 7 10^9+7 109+7取模。

输入描述:

输入只有一行,表示一个整数 N N N ( ( ( 1 1 1 ≤ ≤ N N N ≤ ≤ 1 0 100 10^{100} 10100 ) ) )

输出描述:

输出一个整数,表示答案。

样例输入:

100

样例输出:

967

思路:

一看到这个 N N N的取值范围,又看到和数字有关,那不就是数位 d p dp dp嘛…
首先,我们回想数位 d p dp dp的做法 : : 暴力枚举,使枚举方式满足 d p dp dp数组各维度的性质,最后再记忆化。
数位DP详解
接下来我们就可以看这道题目了,我们确定 d p dp dp数组的第一个维度为 p o s pos pos,表示当前所在的位置。
然后我们很容易想到第二个和第三个维度分别表示 A A A前面的位数和和 B B B前面的位数和,第四个和第五个维度分别表示 A A A的高位是否与 B B B的相同, B B B的高位是否与 N N N相同,那么问题来了,如果这样开数组,那么 d p dp dp数组就是这个样子的:
d p [ 100 ] [ 900 ] [ 900 ] [ 2 ] [ 2 ] dp[100][900][900][2][2] dp[100][900][900][2][2]
可这明显开不下呀!
这时我们就想第二第三个维度是否可以简化?答案是肯定的!我们将二三两个维度压缩成一个维度,表示 A A A前面的位数和 − - B B B前面的位数和,那么 d p dp dp数组就可以变为:
d p [ 100 ] [ 2000 ] [ 2 ] [ 2 ] dp[100][2000][2][2] dp[100][2000][2][2]
解决了 d p dp dp数组的问题,接下来就好办了,具体做法详见代码。

AC Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=105;
const int mod=1e9+7;
int dp[MAXN][MAXN*20][2][2],dig[MAXN],len;
char s[MAXN];
ll dfs(int pos,int sum,int lit1,int lit2)
{
	if(!pos) return sum>1000;//1000的偏移量可以保证差值非负
	if(~dp[pos][sum][lit1][lit2]) return dp[pos][sum][lit1][lit2];//如果已经搜过了就直接返回值
	ll ret=0;
	for(int i=0;i<=(lit1?dig[pos]:9);i++)//限制条件B<=N
		for(int j=0;j<=(lit2?i:9);j++)//限制条件A<=B
			ret=(ret+dfs(pos-1,sum+j-i,lit1&i==dig[pos],lit2&i==j))%mod;
	return dp[pos][sum][lit1][lit2]=ret;//记忆化
}
int main()
{
	memset(dp,-1,sizeof(dp));
	scanf("%s",s);
	len=strlen(s);
	for(int i=0;i<len;i++)
		dig[len-i]=s[i]-'0';
	printf("%lld\n",dfs(len,1000,1,1)%mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值