Description:

有 \(n\) 中烹饪方法和 \(m\) 种食材,要求:

  • 至少做一种菜
  • 所有菜的烹饪方法各不相同
  • 同种食材的菜的数量不能超过总菜数的一半

求做菜的方案数。

Solution1:考虑 DP

先容斥一下,答案为忽略第三个条件所得的方案数减去每一种食材超过一半的方案数之和。

忽略掉第三个条件之后答案显然是

\[\prod_{i=1}^n(1+\sum_{j=1}^m a_{i,j})-1
\]

减去 1 是去掉一道菜都不做的方案。

枚举每一列超过一半的情况,显然,除这一列外,其他 \(n-1\) 列是一样的。那么对于第 \(col\) 列,设 \(f_{i,j,k}\) 表示前 \(i\) 行,第 \(col\) 列选 \(j\) 个且其他列选 \(k\) 个的方案数。则:

\[f_{i,j,k} = f_{i-1,j,k}\text{(不选)}+a_{i,col}*f_{i-1,j-1,k}+(s_i-a_{i,col})*f_{i,-1,j,k-1}
\]

此时的复杂度是 ,\(O(m)\) 的枚举 \(col\) * \(O(n^3)\) 的 \(DP\), = \(O(mn^3)\) ,可以得到 84pts 的好成绩了

Code:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll; const int N = 101;
const int M = 2001;
const int mod = 998244353;
ll n,m;
ll s[N],a[N][M],f[N][N][N];
ll ans=1; void init()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%lld",&a[i][j]);
s[i]=(s[i]+a[i][j])%mod;
}
ans=(ans*(s[i]+1))%mod;
}
ans=(mod-1+ans)%mod;
} int main()
{
init();
for(int col=1;col<=m;++col)
{
memset(f,0,sizeof(f));
f[0][0][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=0;j<=i;++j)
{
for(int k=0;k<=i-j;++k)
{
f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k]*a[i][col]+f[i-1][j][k-1]*(s[i]-a[i][col]);
f[i][j][k]=(f[i][j][k]%mod+mod)%mod;
}
}
} for(int j=1;j<=n;++j)
{
for(int k=0;k<=n-j;++k)
{
if(k<j) ans=((ans-f[n][j][k])%mod+mod)%mod;
}
}
}
printf("%lld\n",ans);
return 0;
}

Solution2:考虑优化

然后我们发现我们并不关心j和k的具体值。我们只关心他们的差。所以我们可以把后两维压缩成一维。

设 \(f_{i,j}\) 表示前 \(i\) 行,第 \(col\) 列比其他列多选 \(j\) 个的方案数。则:

\[f_{i,j} = f_{i-1,j}\text{(不选)}+a_{i,col}*f_{i-1,j-1}+(s_i-a_{i,col})*f_{i,-1,j+1}
\]

此时的复杂度是 ,\(O(m)\) 的枚举 \(col\) * \(O(n^2)\) 的 \(DP\), = \(O(mn^2)\) ,可以得到 100pts 的好成绩了

这里有一个小技巧就是把每个j都加上n,避免数组负下标的出现。

Code:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll; const int N = 101;
const int M = 2001;
const int mod = 998244353;
ll n,m;
ll s[N],a[N][M],f[N][N*2];
ll ans=1; void init()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%lld",&a[i][j]);
s[i]=(s[i]+a[i][j])%mod;
}
ans=(ans*(s[i]+1))%mod;
}
ans=(mod-1+ans)%mod;
} int main()
{
init();
for(int col=1;col<=m;++col)
{
memset(f,0,sizeof(f));
f[0][n]=1;
for(int i=1;i<=n;++i)
{
for(int j=n-i;j<=n+i;++j)//注意dp的范围!
{
f[i][j]=f[i-1][j]+f[i-1][j-1]*a[i][col]+f[i-1][j+1]*(s[i]-a[i][col]);
f[i][j]=(f[i][j]%mod+mod)%mod;
}
} for(int j=1;j<=n;++j)
{
ans=((ans-f[n][n+j])%mod+mod)%mod;
}
}
printf("%lld\n",ans);
return 0;
}

Question:

DP的取值范围问题还是不清楚。

CSP2019 Emiya 家今天的饭的更多相关文章

  1. CSP2019 Emiya 家今天的饭 题解

    这题在考场上只会O(n^3 m),拿了84分.. 先讲84分,考虑容斥,用总方案减去不合法方案,也就是枚举每一种食材,求用它做超过\(\lfloor \frac{k}{2} \rfloor\) 道菜的 ...

  2. csp2019 Emiya家今天的饭题解

    qwq 由于窝太菜了,实在是不会,所以在题解的帮助下过掉了这道题. 写此博客来整理一下思路 正文 传送 简化一下题意:现在有\(n\)行\(m\)列数,选\(k\)个数的合法方案需满足: 1.一行最多 ...

  3. 洛谷P5664 Emiya 家今天的饭 问题分析

    首先来看一道我编的题: 安娜写宋词 题目背景 洛谷P5664 Emiya 家今天的饭[民间数据] 的简化版本. 题目描述 安娜准备去参加宋词大赛,她一共掌握 \(n\) 个 词牌名 ,并且她的宋词总共 ...

  4. 洛谷P5664 Emiya 家今天的饭 题解 动态规划

    首先来看一道题题: 安娜写宋词 题目背景 洛谷P5664 Emiya 家今天的饭[民间数据] 的简化版本. 题目描述 安娜准备去参加宋词大赛,她一共掌握 \(n\) 个 词牌名 ,并且她的宋词总共有 ...

  5. 【NOIP/CSP2019】D2T1 Emiya 家今天的饭

    这个D2T1有点难度啊 原题: 花了我一下午的时间,作为D2T1的确反常 条件很奇怪,感觉不太直观,于是看数据范围先写了个暴力 写暴力的时候我就注意到了之前没有仔细想过的点,烹饪方式必须不同 虽然a很 ...

  6. 【CSP-S 2019】【洛谷P5664】Emiya 家今天的饭【dp】

    题目 题目链接:https://www.luogu.org/problem/P5664 Emiya 是个擅长做菜的高中生,他共掌握 \(n\) 种烹饪方法,且会使用 \(m\) 种主要食材做菜.为了方 ...

  7. 【CSP-S 2019】D2T1 Emiya 家今天的饭

    Description 传送门 Solution 算法1 32pts 爆搜,复杂度\(O((m+1)^n)\) 算法2 84pts 裸的dp,复杂度\(O(n^3m)\) 首先有一个显然的性质要知道: ...

  8. Emiya 家今天的饭

    \(dp_{i,j,k}\)表示前\(i\)种烹饪方法,假设最多的是食材\(j\),食材\(j\)比其他食材多\(k\)次出现 其中\(i \in [1,n],j \in [1,m],k \in [- ...

  9. 【JZOJ6433】【luoguP5664】【CSP-S2019】Emiya 家今天的饭

    description analysis 首先可以知道不符合要求的食材仅有一个,于是可以容斥拿总方案数减去选不合法食材的不合法方案数 枚举选取哪一个不合法食材,设\(f[i][j]\)表示到第\(i\ ...

随机推荐

  1. PHP 超全局变量之$_FILES

    $_FILES——通过 HTTP POST 方式上传到当前脚本的项目的数组. 假设我们上传文件字段name='userfile',$_FILES数组里包括: $_FILES['userfile'][' ...

  2. 安慰奶牛Cheering up the Cow

    传送门 一次a就很开心 可以当作kruskal模板题(orz --------------------------------------------------------------------- ...

  3. UITextField的内存泄漏问题

    背景: 项目中使用了Facebook的FBRetainCycleDetector框架检测内存泄漏问题. 登录VC的view中放置了一个UITextField对象. 产品的要求是当进入登录界面的时候,让 ...

  4. 微信小程序前端坑

    链接:https://www.cnblogs.com/showMagic/p/7677551.html

  5. 国密SM9算法C++实现(Linux)

    首先参考 Linux下编译并使用miracl密码库 该博文在linux下编译Miracl库. 编译完了,自然是要用的,下面介绍两种在C程序中使用miracl库的方法. 方法一: 1. 源码编译完后的必 ...

  6. 如何切换虚拟机(centos6)和windows

    通过设置热键,选择Ctrl+Alt+Fx即可.重启linux之后按Ctrl+Alt+Fx切换不同的终端的就可以了 图一. 图二.

  7. Spring Security 源码分析 --- WebSecurity

    概述 spring security 源码分析系列文章. 源码分析 我们想一下,我们使用 ss 框架的步骤是怎么样的. @Configuration @EnableWebSecurity @Enabl ...

  8. linux 系统 grep 命令 摘录过滤特定的行

    1.grep ‘xxx’ filename  enter   其中xxx为要搜索的字符串 ,即可检索到含有 xxx的行. 2.grep 'xxx' filename  >newfilename ...

  9. Hadoop学习2—伪分布式环境搭建

    一.准备虚拟环境 1. 虚拟环境网络设置 A.安装VMware软件并安装linux环境,本人安装的是CentOS B.安装好虚拟机后,打开网络和共享中心 -> 更改适配器设置 -> 右键V ...

  10. 关于 checkbox 的一些操作

    获取checkbox选中的状态 $("#checkbox").is(":checked"); 设置 checkbox 的状态 $("#checkbox ...