Emiya家今天的饭 NOIP2019 (CSP?) 类DP好题 luoguP5664
首先,硬求可行方案数并不现实,因为不好求(去年考场就这么挂的,虽然那时候比现在更蒟)。
在硬搞可行方案数不行之后,对题目要求的目标进行转换: 可行方案数 = 总方案数 - 不合格方案数。
题目多看几眼,(求最大最小方案数量这种套路),DP无疑。
首先考虑列的限制,发现若有不合法的列,则必然有且只有一列是不合法的:因为不可能有不同的两列数量都超过总数的一半。
于是发现列的合法限制数量可以如此计算:每行选不超过一个的方案数 (总数) - 每行选不超过一个,且某一列选了超过一半的方案数(不合法的)。
重新审查思路:总方案数为固定值,我们要DP的,肯定就是不合格方案数。(废话)
首先肯定是要先设计状态。 怎么样的东东才算是不合格的呢?
重点:: 前 i 行选得比别的列总和还多的那一列是不合法的。(肯定超过一半了)
So, 如果枚举每一列并统计该列不合法的情况,设计状态 f[i][j][k][h] ,即为第h列的前i行,该h列选了 j 个, 别的列总共选了 k 个。
若设si 为每一行的可选的和,
推出状态转移方程: fi,j,k = fi−1,j,k + ai,h∗fi−1,j−1,k + (si−ai,h)∗fi−1,j,k−1
统计该列不合法数量也很简单: Σ(j > k) f[n][j][k][h]
最后将所有列的不合法数量加起来,就能得到总共的不合法数量了。
即为:num = Σ(h = 1~m)Σ(j > k)f[n][j][k][h]
空间能过得去吗?有点悬。
那就用时间换空间吧: 把f的h那一纬去掉,枚举每一行的时候用一个ans统计起来就好了。用完就清空给下一列用。
以为就这么结束了? (D2T1能如此迅速的被A掉也太没面子了吧)
上面做法的复杂度达到了O(MN3)的复杂度,只能拿到84分。M = 500 的几个点过不去。
继续优化呗!!
仔细研究一下,我们DP时的循环时间主要花在了哪里? 相当一部分是花在了 j 和 k 上。
但是,这些时间值得吗?不值得。因为,我们的答案和j 和 k 具体是多少没关系啊!
我们要的,只是j > k 这一个判断式。
所以,想办法优化掉枚举j 和 k 这一步骤,直接转化成大小。
于是,设 f[i][j]为第h行,前i行比别的行的数量多了 j 个。
将原来使用某种主要食材的菜看做使用了两次该食材,并为每种烹饪方式加一种名叫 “不选” 的菜,
使其使用了所有的主要食材各一次。所有使用某种主要食材大于n次的方案即为不合法方案。
最后考虑如何求总和?
easy。 设 g[i][j]为前i行,总共选了 j 个食材的方案数。方程: g[i][j] = g[i-1][j] + (j > 0 ? g[i-1][j-1] * sum[i][0] : 0);
(初始化g[0][0] = 1)
(注意防止数组炸掉,也可以理解为,选择最小只能为0)。
还没完。(真烦)
在进行DP的过程中,可能会出现负数,因此通通打成 (x + mod) % mod.
同时,在计算合法方案数的时候,也将其反过来处理。防止出现玄学的负数
代码来一波呗
#include <bits/stdc++.h>
using namespace std;
#define N 2010
#define N1 105
#define isdigit(c) ((c)>='0'&&(c)<='9')
#define mod 998244353
#define ll long long inline ll read(){
ll x = , s = ;
char c = getchar();
while(!isdigit(c)){
if(c == '-')s = -;
c = getchar();
}
while(isdigit(c)){
x = (x << ) + (x << ) + (c ^ '');
c = getchar();
}
return x * s;
}
/*进行反向操作,求出 总方案数 - 不合法方案数*/
ll sum[N1][N], n, m; /*sum[i] 为第 i 行的和*/
ll a[N1][N], f[N1][N1 << ]; /*枚举每一列: 对于这一列的f[i][j],前i行,比其他列多了j个*/
ll g[N1][N1]; int main(){
n = read(), m = read();
for(int i = ;i <= n; i++)
for(int j = ;j <= m; j++){
a[i][j] = read();
sum[i][] = (sum[i][] + a[i][j]) % mod; /*首先处理出每一行的总和*/
}
for(int i = ;i <= n; i++)
for(int j = ;j <= m; j++){
sum[i][j] = (sum[i][] - a[i][j] + mod) % mod; /*sum[i][j]即为除了这一列之外的一行的和*/
}
ll ans = ;
for(int h = ;h <= m; h++){ /*枚举每一列*/
memset(f, , sizeof(f)); /*不要忘了清理上一列的废 f */
f[][n] = ; /*往上抬 n,防止出现负数*/
for(int i = ;i <= n; i++){
for(int j = n - i;j <= n + i; j++){ /* 乘以a: 其实就是为1就+,为0就-*/
f[i][j] = ((f[i-][j] + f[i-][j-] * a[i][h]) % mod + (f[i-][j+] * sum[i][h]) % mod) % mod;
}
}
for(int i = ;i <= n; i++) /*反向处理*/
ans = (ans + f[n][n+i]) % mod; /*全部方案的和,进行反向处理,防负数*/
}
/*g[i][j]: 前i行选了j个数的方案数. 则总和即为 Σg[n][i] (i = 1~n)*/
g[][] = ;
for(int i = ;i <= n; i++){
for(int j = ;j <= n; j++){
g[i][j] = (g[i-][j] + (j > ? g[i-][j-] * sum[i][] : ) % mod) % mod;
} /*g[i][j]: 方案总和由上一行选j个数的方案数 + 上一行少选一个数 * 这一行的数量组成*/
}
for(int i = ;i <= n; i++){
ans = (ans - g[n][i] + mod) % mod;
}
printf("%lld\n", mod - (ans % mod)); /*代码中进行的是反向处理(防越界)*/
return ;
}
Emiya家今天的饭 NOIP2019 (CSP?) 类DP好题 luoguP5664的更多相关文章
- 洛谷P5664 Emiya 家今天的饭 问题分析
首先来看一道我编的题: 安娜写宋词 题目背景 洛谷P5664 Emiya 家今天的饭[民间数据] 的简化版本. 题目描述 安娜准备去参加宋词大赛,她一共掌握 \(n\) 个 词牌名 ,并且她的宋词总共 ...
- 洛谷P5664 Emiya 家今天的饭 题解 动态规划
首先来看一道题题: 安娜写宋词 题目背景 洛谷P5664 Emiya 家今天的饭[民间数据] 的简化版本. 题目描述 安娜准备去参加宋词大赛,她一共掌握 \(n\) 个 词牌名 ,并且她的宋词总共有 ...
- 【CSP-S 2019】【洛谷P5664】Emiya 家今天的饭【dp】
题目 题目链接:https://www.luogu.org/problem/P5664 Emiya 是个擅长做菜的高中生,他共掌握 \(n\) 种烹饪方法,且会使用 \(m\) 种主要食材做菜.为了方 ...
- 【CSP-S 2019】D2T1 Emiya 家今天的饭
Description 传送门 Solution 算法1 32pts 爆搜,复杂度\(O((m+1)^n)\) 算法2 84pts 裸的dp,复杂度\(O(n^3m)\) 首先有一个显然的性质要知道: ...
- 【NOIP/CSP2019】D2T1 Emiya 家今天的饭
这个D2T1有点难度啊 原题: 花了我一下午的时间,作为D2T1的确反常 条件很奇怪,感觉不太直观,于是看数据范围先写了个暴力 写暴力的时候我就注意到了之前没有仔细想过的点,烹饪方式必须不同 虽然a很 ...
- CSP2019 Emiya 家今天的饭 题解
这题在考场上只会O(n^3 m),拿了84分.. 先讲84分,考虑容斥,用总方案减去不合法方案,也就是枚举每一种食材,求用它做超过\(\lfloor \frac{k}{2} \rfloor\) 道菜的 ...
- Emiya 家今天的饭
\(dp_{i,j,k}\)表示前\(i\)种烹饪方法,假设最多的是食材\(j\),食材\(j\)比其他食材多\(k\)次出现 其中\(i \in [1,n],j \in [1,m],k \in [- ...
- csp2019 Emiya家今天的饭题解
qwq 由于窝太菜了,实在是不会,所以在题解的帮助下过掉了这道题. 写此博客来整理一下思路 正文 传送 简化一下题意:现在有\(n\)行\(m\)列数,选\(k\)个数的合法方案需满足: 1.一行最多 ...
- 【JZOJ6433】【luoguP5664】【CSP-S2019】Emiya 家今天的饭
description analysis 首先可以知道不符合要求的食材仅有一个,于是可以容斥拿总方案数减去选不合法食材的不合法方案数 枚举选取哪一个不合法食材,设\(f[i][j]\)表示到第\(i\ ...
随机推荐
- POJ 2955 区间DP必看的括号匹配问题,经典例题
Brackets Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14226 Accepted: 7476 Description ...
- USACO Training Section 1.1黑色星期五Friday the Thirteenth
题目描述 13号又是一个星期五.13号在星期五比在其他日子少吗?为了回答这个问题,写一个程序,要求计算每个月的十三号落在周一到周日的次数.给出N年的一个周期,要求计算1900年1月1日至1900+N- ...
- shell之路 shell核心语法【第一篇】shell初识
shell简介 1.Shell是Unix的脚本语言,是与 Unix/Linux 交互的工具,shell使用的熟练程度反映了用户对Unix/Linux使用的熟练程度 2.Shell是系统命令+程序逻辑的 ...
- 「译」JVM是如何使用那些你从未听过的x86魔幻指令实现String.compareTo的
原文https://jcdav.is/2016/09/01/How-the-JVM-compares-your-strings/ 魔幻的String.compareTo 我们之前可能已经见过Java的 ...
- 201771010113 李婷华 《面向对象程序设计(Java)》第十一周总结
一.理论知识部分 第九章 集合 1.数据结构介绍: 线性结构:线性表,栈,队列,串,数组,文件.非线性结构:树,图. 散列表:又称为哈希表. 散列表算法的基本思想是:以结点的关键字为自变量,通过一定 ...
- Spring中bean的四种注入方式
一.前言 最近在复习Spring的相关内容,这篇博客就来记录一下Spring为bean的属性注入值的四种方式.这篇博客主要讲解在xml文件中,如何为bean的属性注入值,最后也会简单提一下使用注解 ...
- ubuntu docker相关错误记录
执行下面命令 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 报错: gpg: can't c ...
- 使用plupload实现多文件上传,自定义参数
下载地址:点击打开链接 1.在开发中可能需要用户附件上传的功能,实现批量上传功能其实就将多个上传任务放到一个集合中,分别上传. 2,使用plupload js插件可以很轻松的实现带参数的多文件上传 3 ...
- webpack指南(一)HRM+Tree Shaking
参考:https://www.cnblogs.com/PasserByOne/p/12084323.html https://blog.csdn.net/qq593249106/article/det ...
- ASHRAE KAGGLE大能源预测(前三名方案总结+相关知识点讲解+python实现)
@ 目录 1 概述 2 处理思想学习 2.1 移除异常值 2.2 缺失值 2.3 目标函数 2.4 特征工程 2.4.1 Savitzky-Golay filter 2.4.2 Bayesian ta ...