正题

题目链接:https://www.luogu.com.cn/problem/P6775


题目大意

\(n\)种原材料,第\(i\)个有\(d_i\)个,\(m\)道菜品都需要\(k\)个原料而且每道菜最多只能用两种材料。

要求构造方案使得满足条件。

\(1\leq n\leq 500,n-2\leq m\leq 5000,1\leq k\leq 5000,(\sum_{i=1}^nd_i)=m\times k\)


解题思路

额去年线上赛的时候一点想法都没有是时候轮到我来一雪前耻历(虽然这题还是很难

首先我们注意到一个特殊的条件\(n-2\leq m\),看上去没什么想法但是看一下数据范围有\(n-1=m\)和\(n-1\leq m\)两个部分分。

先考虑\(n-1=m\)的,就是原料比菜品多一个,那么我们一定有\(d_{min}<k\)而且\(d_{min}+d_{max}>k(n\neq 2)\)。

嗯所以我们每次拿最少的一个原料和最多的一个原料做一个菜那么依旧满足\(n-1=m\)的条件。

然后考虑\(n\leq m\)的情况,不难发现肯定有\(d_{max}\geq k\),所以我们直接拿最多的来做一道菜那么要不\(n-1,m-1\)要么\(m-1\)变成\(n-1=m\)的情况。

之后是\(n-2=m\)的做法,这个是本题的核心难点。

在洛谷题解上看到过一个有趣的证明,我们可以把原料看成一个点,菜品所用的两个原料看成一条边,那么因为\(m=n-2\)所以这张图一定是不连通的,那么至少会被分成两棵树。

发现对于树就是\(m=n-1\)的情况,所以其实是相当于我们要把\(m=n-2\)的情况分成两个\(m=n-1\)的情况。

而且因为\(m=n-1\)一定有解所以我们只需要考虑怎么分就好了。

相当于我们要找出一个原料集合\(S\)使得

\[\sum_{i\in S}d_i=(|S|-1)k\Rightarrow \sum_{i\in S}(d_i-k)=-k
\]

然后直接\(dp\)的复杂度是\(O(Tn^2k)\)的,加个\(bitset\)优化就是\(O(T\frac{n^2k}{w})\),可以通过本题


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<bitset>
using namespace std;
const int N=510,S=5e6+10,W=2500000;
struct node{
int w,id;
}d[N];
struct cnode{
int a,A,b,B;
}v[N*10];
int T,n,m,k,tot;
bool vis[N];
bitset<S>f[N];
vector<node> u;
bool cmp(node x,node y)
{return x.w<y.w;}
void Add(int a,int A,int b=0,int B=0)
{v[++tot]=(cnode){a,A,b,B};return;}
void solve(int m,vector<node> &d){
int n=d.size();tot=0;
while(m&&m>=n){
sort(d.begin(),d.end(),cmp);
Add(d[n-1].id,k);
d[n-1].w-=k;m--;
if(!d[n-1].w)d.pop_back(),n--;
}
if(m==n-1){
while(m){
sort(d.begin(),d.end(),cmp);swap(d[0],d[n-1]);
Add(d[n-1].id,d[n-1].w,d[0].id,k-d[n-1].w);
d[0].w-=k-d[n-1].w;d.pop_back();n--;m--;
if(!d[0].w)swap(d[0],d[n-1]),d.pop_back(),n--;
}
}
for(int i=1;i<=tot;i++){
if(v[i].b)printf("%d %d %d %d\n",v[i].a,v[i].A,v[i].b,v[i].B);
else printf("%d %d\n",v[i].a,v[i].A);
}
u.clear();return;
}
int main()
{
scanf("%d",&T);
f[0][W]=1;
while(T--){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
scanf("%d",&d[i].w),d[i].id=i;
if(m==n-2){
for(int i=1;i<=n;i++){
int tmp=d[i].w-k;
if(tmp>=0){
f[i]=f[i-1];
f[i]|=(f[i-1]<<tmp);
}
else{
f[i]=f[i-1];
f[i]|=(f[i-1]>>(-tmp));
}
}
if(!f[n][W-k])puts("-1");
else{
memset(vis,0,sizeof(vis));
for(int i=n,z=W-k;i>=1;i--){
int tmp=d[i].w-k;
if(f[i-1][z-tmp])
u.push_back(d[i]),vis[i]=1,z-=tmp;
}
solve(u.size()-1,u);
for(int i=1;i<=n;i++)
if(!vis[i])u.push_back(d[i]);
solve(u.size()-1,u);
}
}
else{
for(int i=1;i<=n;i++)
u.push_back(d[i]);
solve(m,u);
}
}
return 0;
}

P6775-[NOI2020]制作菜品【贪心,dp】的更多相关文章

  1. 洛谷 P6775 - [NOI2020] 制作菜品(找性质+bitset 优化 dp)

    题面传送门 好久没写过题解了,感觉几天没写手都生疏了 首先这种题目直接做肯定是有些困难的,不过注意到题目中有个奇奇怪怪的条件叫 \(m\ge n-2\),我们不妨从此入手解决这道题. 我们先来探究 \ ...

  2. [NOI2020] 制作菜品

    看懂题目是生产第一要素. 考虑\(m = n - 1\)则必定有解.我们每次选择最小的和最大的拼在一起即可. 当\(m\)大于\(n\),那么我们只要每次选择最大的给他消掉即可. \(m = n - ...

  3. 【BZOJ-3174】拯救小矮人 贪心 + DP

    3174: [Tjoi2013]拯救小矮人 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 686  Solved: 357[Submit][Status ...

  4. BZOJ_3174_[Tjoi2013]拯救小矮人_贪心+DP

    BZOJ_3174_[Tjoi2013]拯救小矮人_贪心+DP Description 一群小矮人掉进了一个很深的陷阱里,由于太矮爬不上来,于是他们决定搭一个人梯.即:一个小矮人站在另一小矮人的 肩膀 ...

  5. 洛谷P4823 拯救小矮人 [TJOI2013] 贪心+dp

    正解:贪心+dp 解题报告: 传送门! 我以前好像碰到过这题的说,,,有可能是做过类似的题qwq? 首先考虑这种显然是dp?就f[i][j]:决策到了地i个人,跑了j个的最大高度,不断更新j的上限就得 ...

  6. 【bzoj5073】[Lydsy1710月赛]小A的咒语 后缀数组+倍增RMQ+贪心+dp

    题目描述 给出 $A$ 串和 $B$ 串,从 $A$ 串中选出至多 $x$ 个互不重合的段,使得它们按照原顺序拼接后能够得到 $B$ 串.求是否可行.多组数据. $T\le 10$ ,$|A|,|B| ...

  7. 【bzoj3174】[Tjoi2013]拯救小矮人 贪心+dp

    题目描述 一群小矮人掉进了一个很深的陷阱里,由于太矮爬不上来,于是他们决定搭一个人梯.即:一个小矮人站在另一小矮人的 肩膀上,知道最顶端的小矮人伸直胳膊可以碰到陷阱口.对于每一个小矮人,我们知道他从脚 ...

  8. hdu 1257 最少拦截系统【贪心 || DP——LIS】

    链接: http://acm.hdu.edu.cn/showproblem.php?pid=1257 http://acm.hust.edu.cn/vjudge/contest/view.action ...

  9. 贪心+DP【洛谷P4823】 [TJOI2013]拯救小矮人

    P4823 [TJOI2013]拯救小矮人 题目描述 一群小矮人掉进了一个很深的陷阱里,由于太矮爬不上来,于是他们决定搭一个人梯.即:一个小矮人站在另一小矮人的 肩膀上,知道最顶端的小矮人伸直胳膊可以 ...

  10. 贪心+dp

    贪心+dp 好多题都是这个思想, 可以说是非常重要了 思想一: 在不确定序列无法dp的情况下, 我们不妨先假设序列已经选定, 而利用贪心使序列达到最优解, 从而先进行贪心排序, 在进行dp选出序列 思 ...

随机推荐

  1. git flow版本

    feature 分支:开发者进行功能开发的分支. develop 分支:对开发的功能进行集成的分支. release 分支:负责版本发布的分支. hotfix 分支:对线上缺陷进行修复工作的分支,热修 ...

  2. 2、二进制安装K8s 之 部署ETCD集群

    二进制安装K8s 之 部署ETCD集群 一.下载安装cfssl,用于k8s证书签名 二进制包地址:https://pkg.cfssl.org/ 所需软件包: cfssl 1.6.0 cfssljson ...

  3. 栈编程和函数控制流: 从 continuation 与 CPS 讲到 call/cc 与协程

    原标题:尾递归优化 快速排序优化 CPS 变换 call/cc setjmp/longjmp coroutine 协程 栈编程和控制流 讲解 本文为部分函数式编程的扩展及最近接触编程语言控制流的学习和 ...

  4. QT系列

    大话QT系列:http://blog.csdn.net/houqd2012/article/category/2128295 里面有提到CTK插件系统.

  5. 云原生数据库 TDSQL-C 产品概述、产品优势、应用场景

    云原生数据库 TDSQL-C(Cloud Native Database TDSQL-C,TDSQL-C)是腾讯云自研的新一代高性能高可用的企业级分布式云数据库.融合了传统数据库.云计算与新硬件技术的 ...

  6. IDEA中的Git操作,看这一篇就够了!

    大家在使用Git时,都会选择一种Git客户端,在IDEA中内置了这种客户端,可以让你不需要使用Git命令就可以方便地进行操作,本文将讲述IDEA中的一些常用Git操作. 环境准备 使用前需要安装一个远 ...

  7. 基于Linux的系统排错

    1.系统引导过程概述 2.系统异常及恢复 [1]grub系统引导 1)mbr上446字节丢失 模拟问题: dd if=/dev/zero? of=/dev/vda? bs=446? count=1? ...

  8. vue 引用省市区三级联动(element-ui select)

    npm 下载 axios npm install --save axios static 静态文件夹里 创建 json 文件夹 json 文件夹里创建 map.json map.json 文件里写 ( ...

  9. Linux学习笔记 - Linux快捷操作及常用命令

    一.快捷键 剪切光标前的内容 Ctrl + u 剪切光标至行末的内容 Ctrl + k 粘贴 Ctrl + u 或 Ctrl +k 的内容 Ctrl + y 移动光标到行末 Ctrl + e 移动光标 ...

  10. openwrt开发笔记二:树莓派刷openwrt

    前言及准备 本笔记适用于第一次给树莓派刷openwrt系统的玩家,对刷机过程及注意事项进行了记录,刷机之后对openwrt进行一些简单配置. 使用openwrt源码制作固件需要花费一点时间. 平台环境 ...