题解:P2409 Y的积木

· · 题解

这边是题目传送门喵!

欢迎到博客园阅读!

题意简述

## 思路 先考虑最暴力的方法:枚举所有情况,然后取最小值。复杂度 $\Theta(m^n)$。 考虑优化。想到了[序列合并](https://www.luogu.com.cn/problem/P1631)这一题,其实两者有共通之处。当 $n=2$ 时其实就几乎是一个题了。如果两行的数量不一样和选出前 $k$,在原题基础上其实很好修改。 对于两行,我们存储可能进入前 $k$ 的和加入优先队列,然后取优先队列中最小的 $k$ 个作为真正的前 $k$ 即可。假设一行是 $a$,一行是 $b$,目前的和为 $x=a_i+b_j$,则一定比它小的和有 $j\times(i-1)+j-1$ 个(理由是:前 $i-1$ 行一定有 $j\times(i-1)$ 个小于 $x$,而本行一定有 $j-1$ 个小于 $x$),则在最优情况下其排名为 $j\times(i-1)+j$,当这个数不超过 $k$ 即可能进入前 $k$。 以上步骤复杂度我也不知道,但大致是 $\Theta(k \log k)$。于是做完了 $n=2$ 的情况,接下来推广到一般。 不难发现,如果在我选到第 $i$ 行时,某种选法就已经不是前 $k$ 了,那无论如何在后面都无法重新进入前 $k$(这是显然的,后面每一个盒子都选最小值即可)。 同时发现当 $m>k$ 时只有前 $k$ 个产生贡献。我们用一个数组 $f$ 存储 $1\sim i-1$ 行的前 $k$。对于每一个 $i\geq 2$,都将 $1\sim i-1$ 行的前 $k$ 视为一行,第 $i$ 行视为另一行。对这两行做一遍上面 $n=2$ 的操作,然后将得出来的新的前 $k$ 覆盖到 $f$ 数组上。 最后一次操作完得到的 $f$ 数组就是答案。 ## 代码 细节问题: 1. 为了实现方便,对照[序列合并](https://www.luogu.com.cn/problem/P1631)一题的格式,将两行的个数都默认为 $k$,将不存在的位置设置为 $+\infty$。 2. 记得每一行处理完之后要清空优先队列。 ```cpp #include <bits/stdc++.h> #define loop(i,a,b) for(int i=(a);i<=(int)(b);i++) using namespace std; const int N=1e4+5; const int inf=0x3f3f3f3f; int n,m,k,a[N],f[N]; priority_queue<int,vector<int>,greater<int> >q; int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>k; loop(t,1,n){ cin>>m; loop(i,1,m)cin>>a[i]; loop(i,m+1,k)a[i]=inf; sort(a+1,a+1+m); if(t==1){ loop(j,1,k)f[j]=a[j]; continue; } loop(i,1,min(m,k)){ for(int j=1;j*(i-1)+j<=k;j++)q.push(a[i]+f[j]); } loop(i,1,k)f[i]=q.top(),q.pop(); while(!q.empty())q.pop(); } loop(i,1,k)cout<<f[i]<<' '; return 0; } ``` 完结撒花花喵~