2020牛客暑期多校训练营Harmony Pairs(数位DP)

Harmony Pairs

题目描述

在这里插入图片描述

输入描述:

在这里插入图片描述

输出描述:

在这里插入图片描述

示例1

输入

100

输出

967

题目大意

定义一个函数 S ( n ) S(n) S(n)表示 n n n的各位数之和。现要求在 1 ∼ n 1\sim n 1n之间有多少数对 ( a , b ) (a,b) (a,b)满足 S ( a ) > S ( b ) S(a)>S(b) S(a)>S(b),并且 0 ≤ a ≤ b ≤ n 0\le a\le b\le n 0abn

分析

十分亮眼的是, n ≤ 1 0 100 n\le 10^{100} n10100。又看到各位数之和,自然想到数位 d p dp dp。这题主要是有两个数要进行数位 d p dp dp,所以比较烦人。但是代码是短的

先考虑 d p dp dp的定义,第一维肯定是 p o s pos pos表示第 p o s pos pos位,然后考虑两个数的 s u m sum sum,但是两个数各位的 s u m sum sum最大有 900 900 900 900 ∗ 900 ∗ 100... 900*900*100... 900900100...这样的空间肯定是炸了啊,因此考虑压缩。

因为文中只要比较大小即可,所以我第二维只要存一下 a a a b b b的差就可以了。为了防止差为负数,要加1000的偏移量。

因此, d p [ p o s ] [ d i f f ] [ l m t a ] [ l m t b ] dp[pos][diff][lmta][lmtb] dp[pos][diff][lmta][lmtb]表示 p o s pos pos位时, a , b a,b a,b的差为 d i f f diff diff a a a是否到极限, b b b是否到极限的满足题意的数对数目。

转移式见代码注释。

代码

#include
#define ll long long
using namespace std;
const int MAXN=110;
const int mod=1e9+7;
char N[MAXN];
int dp[MAXN][MAXN*20][2][2],n[MAXN];
int dfs(int pos,int diff,bool lmta,bool lmtb){
//	cerr<
	if(!pos) return diff>1000;//由于偏移,因此0变成1000
	if(~dp[pos][diff][lmta][lmtb]) return dp[pos][diff][lmta][lmtb];
	//记忆化
	int ret=0;
	for(int i=0;i<=(lmtb?n[pos]:9);i++){//枚举b的pos位,如果是极限,那么应当是n的pos位为极限
		for(int j=0;j<=(lmta?i:9);j++){//枚举a的pos位,同上
			ret=(ret+dfs(pos-1,diff+j-i,lmta&(j==i),lmtb&(i==n[pos])))%mod;
			//加上a减去b,若a>b则其值为正,并更新极限
		}
	}
	return dp[pos][diff][lmta][lmtb]=ret;//记忆化
}
int main()
{
	memset(dp,-1,sizeof(dp));//初始化-1
	scanf("%s",N);
	int len=strlen(N);
	for(int i=0;i<len;i++) n[len-i]=N[i]-'0';//字符串输入,塞到数组里。
	printf("%d\n",dfs(len,1000,1,1));//调用
}

END

难得,比较顺利地AC了一道 d p dp dp

你可能感兴趣的:(2020牛客多校,数位DP)