步骤
打表找性质,优化数学模型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#include 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; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)