【ybtoj高效进阶 21282】数字重组(DP)(数学)

数字重组

题目链接:ybtoj高效进阶 21282

题目大意

给你一个数组,再给出一个数 k,保证数组长度是 k 的倍数。
然后要你把数组分成 k 个集合,定义一个分法的价值是它 k 个集合的极差之和,然后要你找价值最小的分发,输出其价值。

思路

考虑从小到大枚举数字。

考虑先把 k 个集合弄出来,以它们的个数弄成一个 vector 作为状态。
那如果你接下来加的集合是空的,那我们就会有 ai 的贡献,如果你加了之后集合满了,就有 ai 的贡献。(因为你数字是从小到大依次加入)
那显然我们发现顺序不同的仍是同一个集合,所以我们可以排序,以减小状态数。

但接着有一个问题,它要划分成集合,也就是不能有重复的数字被划入同一个集合中。
那我们考虑设 fi,j,S 为搞定到第 i 个数,当前状态集合是 S,放 i 之前 i 这个位置的数所在集合的最小大小是 j。那我们就选当前个数不超过 j 的来放即可,因为如果之前有了那现在它的个数肯定是 j+1

那我们就再转移,看看复杂度,首先要算的是集合的个数。
它相当于从 (0,0) 走到 (k,nk) 的折线。
所以你就是把向右向上的操作随便放,就是组合数 Ck+nkk

不难看出当 k=n 的时候,状态数最多,为 C2nn

然后再乘上 nk,复杂度可以过。

代码

#include<map> #include<vector> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; int n, k, a[101]; map <vector<int>, ll> now, nxt; vector <int> emp; ll ans; int main() { // freopen("num.in", "r", stdin); // freopen("num.out", "w", stdout); scanf("%d %d", &n, &k); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); sort(a + 1, a + n + 1); emp.push_back(k); for (int i = 1; i <= k; i++) emp.push_back(0); now[emp] = 0; for (int i = 1; i <= n; i++) { nxt.clear(); for (map <vector<int>, ll> ::iterator it = now.begin(); it != now.end(); it++) { vector <int> A = (*it).first; ll B = (*it).second; for (int j = 1; j <= k; j++) { if (A[j] == n / k) continue; if (a[i] == a[i - 1] && A[j] > A[0]) continue; vector <int> toA = A; ll toB = B; toA[j]++; if (toA[j] == 1) toB -= a[i]; if (toA[j] == n / k) toB += a[i]; toA[0] = 0; sort(toA.begin(), toA.end());//记得重新排序 toA[0] = A[j]; if (nxt.find(toA) == nxt.end()) nxt[toA] = toB; else nxt[toA] = min(nxt[toA], toB); } } swap(now, nxt);//滚动数组 } ans = 1e15; for (map <vector<int>, ll> ::iterator it = now.begin(); it != now.end(); it++) { ans = min(ans, (*it).second); } printf("%lld", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21282.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(71)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示