牛客寒假训练营 1 A 九小时九个人九扇门(01背包,打表找规律)

步骤

  • 打表找性质,优化
  • 数学模型
  • dp

优化

遇事不决打个表,先打表模拟样例找规律

右边为数字根,可以发现规律,9进制,逢9进一

那么数字根==(数)%9

特殊点,当余数==0 时 ,数字根为 9

验证

验证出,对于前面所有的数字和,第i个数的数字根+数字和的根 == (第i个数+数字和)的数字根  

数学模型

题目要求枚举出所有种组合

每个组合都当且仅有两个选择 选 或者 不选

直接枚举肯定会超时,想到dp也可以达到枚举的效果,只是记录下了重复的步骤,所以采用dp算法,就是01背包模型

dp

 

原问题:从n个人中选,数字根为1,2,3,...的所有方案

子问题:从前i个人种选,数字根为j的所有方案

集合:所有从i个人中选,数字根为j的所有方案

边界:0

初始化:根据拓扑序列,入度为0的点为起点,存在以该点为起点的拓扑序列,以该点为起点的拓扑序列可以延伸到许多点,对于所有入度为0的点,我们都需要初始化方案数为1,所以应该++

所以前面还没有选人,只选了第i个人的方案应该计为1 ,即dp[i][a[i]%9]++;

状态计算:根据最后一个状态,选或者不选,可以分为两个子集

  • 当前选了第i个物品
  • 当前没有选第i个物品

由于本题对于逆拓扑计算很难划分,可以采用正拓扑的计算方法,从第i-1的角度看会递推到那两个集合中

  • dp[i-1][j]---->选     dp[i][(j+a[i])%9]
  • dp[i-1][j]---->不选  dp[i][j]
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
const ll mod = 998244353;
int a[N];
ll dp[N][10];
int  get_mod(int a)
{
    int t= (a % 9 + 9) % 9;
    if (t == 0) return 9;
    else return t;
}
int main()
{
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++)
        cin >> a[i];
    //for(int i=0;i<=9;i++)
      //  dp[0][i]=1;
    //dp[0][0] = 1;
    for (int i = 1;i <= n;i++)
    {   
        dp[i][get_mod(a[i])]++;
        for (int j = 1;j <= 9;j++)
        {
            int t = get_mod(j + a[i]);
            dp[i][t] = (dp[i][t] + dp[i - 1][j]) % mod;
            dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod;
        }
    }
    for (int i = 1;i <= 9;i++)
        cout << dp[n][i] << ' ';
    return 0;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值