牛客第六场补题 数位dp Harmony Pairs

本文介绍了如何利用数位动态规划方法解决Harmony Pairs问题,该问题要求找到0 <= A <= B <= n中S(A) > S(B)的数对(A, B)。文章提供了解题思路及状态转移方程。" 104454496,5568217,TensorFlow实战经验与技巧,"['tensorflow', '深度学习', '卷积神经网络', '优化算法', '模型训练']
摘要由CSDN通过智能技术生成

Harmony Pairs

题目链接

题目大意

S(A) 代表A的每位数之和。
求0 <= A <= B <= n 并且 S(A) > S(B) 的(A,B)对的数量。

题解

看到这道题,首先想到数位dp,然后就不会了。。
状态表示:dp[i][j][f1][f2] 表示当前是第i位(也就是前i位的状态已经计算过来了。现在往i + 1位转移),
S(A) - S(B) = j ,
f1表示A与B的大小关系:A < B的时候f1是1 A == B 时 f1是0 ,A不能大于B
f2表示B与N的大小关系:B < N的时候f2是1 N== B 时 f2是0 ,B不能大于N
状态转移:
dp[i + 1][j + a - b][f1][f2] = (dp[i + 1][j + a - b][f1][f2] + dp[i][j][f][k])%mod;

代码:

#include<algorithm>
#include<cstring>
#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void wenjian(){freopen("concatenation.in","r",stdin);freopen("concatenation.out","w",stdout);}
void tempwj(){freopen("hash.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second < b.second;}};
int lb(int x){return  x & -x;}
//friend bool operator < (Node a,Node b)   重载
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int maxn = 5e5+10;

char a[maxn];
ll dp[105][2000][2][2];  // dp[i][j][f0][f1] 表示前i位 差是j a跟b的大小关系  n跟b的大小关系
//sA - sB
//A < B  1
//a == b 0
//B < N  1
//B == N 0
// 因为最后要求a<b<=n的
int num[maxn];
int main()
{
	scanf("%s",a + 1);
	int n = strlen(a + 1);
	dp[0][1000][0][0] = 1;
	for (int i = 1; i <= n; i ++ )
	{
		num[i] = a[i] - '0';
	}
	for (int i = 0; i < n; i ++ )
	{
		int s = num[i + 1];
		for (int j = 0; j <= 2000; j ++ )
		{
			for (int f = 0; f < 2; f ++ )
			{
				for (int k = 0; k < 2; k ++ )
				{
					if(dp[i][j][f][k] == 0)
						continue;
					for (int a =0 ; a < 10; a ++ )
					{
						for (int b = 0; b < 10; b ++ )
						{
							if(a > b && f == 0)//A 不能大于 B
								continue;
							if(b > s && k == 0)//B 不能大于 N
								continue;
							int f1 = f;
							int f2 = k;
							if(a < b)
							{
								f1 |= 1;
							}
							if(b < s)
								f2 |= 1;
							dp[i + 1][j + a - b][f1][f2] = (dp[i + 1][j + a - b][f1][f2] + dp[i][j][f][k])%mod;
						}
					}
				}
			}
		}
	}
	ll ans = 0;
	for (int i = 1001; i <= 2000; i ++ )
	{
		for (int j =0 ; j < 2; j ++ )
		{
			for (int k = 0; k < 2; k ++ ) // 其实j == 0 没有用。。直接加dp[n][i][1][k] 就好 因为A == B 时S(A) == S(B)
				ans = (ans + dp[n][i][j][k]) % mod;
		}
	}
	printf("%lld\n",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值