九小时九个人九扇门(01背包)

题目

原题链接


分析

根据题目可以发现,一个数 ( 1 ≤ n ≤ 1 0 5 ) (1\leq n\leq 10^5) (1n105)的数字根其实就是这个数按 9 9 9取模的值 ( 一 个 数 取 模 结 果 为 0 说 明 这 个 数 的 数 字 根 是 9 ) (一个数取模结果为0说明这个数的数字根是9) (09).
题目问的是几个数之和的数字根,按照模运算的性质: ( a + b ) % p = ( a % p + b % p ) % p (a+b)\%p=(a\%p+b\%p)\%p (a+b)%p=(a%p+b%p)%p
依然是几个数的数字根之和的取模结果,所以我们在一开始就可以对数据全部进行取模操作。

确定思路,题目问的是组合数,在每一种组合中,其实对于个体来说只有两种情况,那么这道问题跟之前写的砝码称重有点像,都是01背包的问题。

步骤一:确定状态
步骤二:确定状态转移方程
步骤三:确定边界情况和初始条件
步骤四:确定计算顺序

步骤一:确定状态
d p [ i ] [ j ] dp[i][j] dp[i][j]表示在处理完第 i i i个人后第 j j j种情况的对应组合种数。
步骤二:确定状态转移方程
已知在处理完第 i − 1 i-1 i1个人后所有情况的对应组合种数,那么想要求 d p [ i ] [ j ] dp[i][j] dp[i][j]
d p [ i ] [ j ] = ( d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ ( j + 9 − a [ i ] ) % 9 ] ) % m o d dp[i][j]=(dp[i-1][j]+dp[i-1][(j+9-a[i])\%9])\%mod dp[i][j]=(dp[i1][j]+dp[i1][(j+9a[i])%9])%mod

d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]表示第 i i i个人不参加组合,继承 i − 1 i-1 i1个人的条件下情况 j j j对应的组合种数;
d p [ i − 1 ] [ ( j + 9 − a [ i ] ) % 9 ] dp[i-1][(j+9-a[i])\%9] dp[i1][(j+9a[i])%9]表示第 i i i个人参加组合后,由情况 j − a [ i ] j-a[i] ja[i]变为情况 j j j要考虑取模。
步骤三:确定边界情况和初始条件
d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1
步骤四:确定计算顺序
自上往下。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define fir(i,a,b) for (int i = (a); i <= (b); i++)
const ll N=1e5+9;
const ll mod = 998244353;
ll dp[N][9];
ll a[N];
ll n;
int main(){
	cin>>n;
	dp[0][0]=1;
	fir(i,1,n){
		cin>>a[i];
		a[i]%=9;
		fir(j,0,8){
			dp[i][j]=(dp[i-1][j]+dp[i-1][(j+9-a[i])%9])%mod;
		}			
	}
	fir(i,1,8)
		cout<<dp[n][i]<<" ";
	cout<<dp[n][0]-1;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值