ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P5664[CSP-S2019] Emiya 家今天的饭 (dp + 计数)

2022-09-05 20:33:15  阅读:38  来源: 互联网

标签:烹饪 P5664 int sum 菜品 食材 ans Emiya S2019


 

P5664[CSP-S2019] Emiya 家今天的饭 (dp + 计数)

题目传送门

题目大意:

给定一个大小为 \(n * m\) 的表格 , 其中 \(a_{i , j}\) 表示用第 \(i\) 种烹饪方式并且有第 \(j\) 种主要食材的不同菜品的数量,找出至少有一种菜品,每种菜品的烹饪方式不同且满足,所有的主要食材的出现次数不超过总菜品数的一半的方案数。

题目分析:
  • [\(1\)] : 因为每种菜品的烹饪方式均不同,那么我们想到可以枚举烹饪方式。

  • [\(2\)] : 正难则反。我们对于方案数可以很快速的求出总的方案数,那我么可以想到用总的方案数减去不符合的方案数。

  • [\(3\)] : 设总方案数为 \(ans\) , 那么我们先考虑每一行的方案数,对于每一行只能选取一种,故每行方案数为这种烹饪方式所能做的所有菜品,我们令 \(sum_i\) 为每行的总数量,那么 \(ans\) 是否等于 \(\prod_{i = 1} ^ n sum_i\) 呢? 答案是否定的,因为对于每一行我们还可以不选,所以 \(ans = \prod_{i = 1} ^ n ({sum_i +1})\) , 在 \(ans\) 中还包含了一个什么也不选的方案,所以还需要将 \(ans\) 减 \(1\)。

  • [\(4\)] : 考虑完第一个以及第二个条件,我们再来考虑不满足第三个条件的方案数,由于遍历有一定的顺序,所以我们考虑使用 \(dp\) 来求解 。

  • [\(5\)] : 首先我们考虑最暴力的做法,根据什么不好处理就将其放到状态中的方法, 我们令 \(f_{i , j, k , l}\) 为前 \(i\) 中烹饪方式中,选了 \(k\) 个菜品 , \(j\) 食材出现了 \(l\) 次的方案数。 那么很好得出: \(f_{i,j,k,l} = f_{i - 1 , j,k,l} + a_{i,j} * f_{i - 1,j,k - 1,l - 1} + (\Sigma_{x=1}^n a_{i,x} - a_{i,j}) * f_{i - 1,j,k - 1,l}\)。 其中 \(\Sigma_{x=1}^n a_{i,x} - a_{i,j}\) 可以变为 \(sum_i - a_{i,j}\)。所以 : \(f_{i,j,k,l} = f_{i - 1 , j,k,l} + a_{i,j} * f_{i - 1,j,k - 1,l - 1} + (sum_i - a_{i,j}) * f_{i - 1,j,k - 1,l}\)
    复杂度为 \(O(n^3m)\)

  • [\(6\)] : 根据转移式子我们可以发现,注意的复杂度已经降为 \(O(1)\) ,故我们考虑去优化状态,因为题目中要求最多的食材不超过所选菜品的一半,所以我们直接维护这种食材与其他食材的差即可 ,所以我们令 \(f_{i,j,k}\) 为前 \(i\) 中烹饪方式,\(j\) 食材的数量与其他所有食材的数量之差为 \(k\)。 那么转移方程为: \(f_{i,j,k} = f_{i - 1,j,k} + a_{i,j} * f_{i - 1,j,k - 1} + (sum_i - a_{i,j}) * f_{i - 1,j,k + 1}\),我们又发现每次转移时 \(j\) 是不发生变化的,所以我们考虑将 \(j\) 这一维去掉,我们可以在最外层枚举第 \(j\) 种食材,然后就降维了,最后我们只需要让 \(ans\) 见到所有 \(k > 0\) 的情况即可。

代码实现:

因为 \(k\) 可能会出现负的,所以我们让 \(k\) 都去加上 \(n\) 来保证 \(k\) 都是正的,其他细节见代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
const int M = 2e3 + 7;
int a[200][M] , sum[200];
int n , m; 
int dp[200][500];
int get_id(int x) {//这里就是将 k 变为正的的
	return x + n + 1;
}
signed main () {
	ios::sync_with_stdio(0),cin.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= n; ++ i) for(int j = 1; j <= m; ++ j) {cin >> a[i][j];sum[i] += a[i][j];sum[i] %= mod;}
	int ans = 1;
	for(int i = 1; i <= n; ++ i) ans = (ans * (sum[i] + 1)) % mod;//求出总方案数
	ans = (ans - 1 + mod) % mod;//减掉一个都不选的
	for(int i = 1; i <= m; ++ i) {
		memset(dp , 0 , sizeof dp);
		dp[0][get_id(0)] = 1;//初值
		for(int j = 1; j <= n; ++ j) 
			for(int k = -1 * n; k <= n; ++ k) 
				dp[j][get_id(k)] = (dp[j - 1][get_id(k)] + a[j][i] * dp[j - 1][get_id(k - 1)] % mod + ((sum[j] + mod - a[j][i]) * dp[j - 1][get_id(k + 1)]) % mod) % mod;
		for(int k = 1; k <= n; ++ k) ans = (ans - dp[n][get_id(k)] + mod) % mod;
	}
	cout << ans;
	return 0;	
}

[========]

完结!

标签:烹饪,P5664,int,sum,菜品,食材,ans,Emiya,S2019
来源: https://www.cnblogs.com/Love-yx/p/16659366.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有