ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-10-27 07:31:40  阅读:203  来源: 互联网

标签:nxt now 进阶 ybtoj ll 21282 include 集合 toA


数字重组

题目链接:ybtoj高效进阶 21282

题目大意

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

思路

考虑从小到大枚举数字。

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

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

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

不难看出当 \(k=\sqrt{n}\) 的时候,状态数最多,为 \(C_{2\sqrt{n}}^{\sqrt{n}}\)。

然后再乘上 \(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;
}

标签:nxt,now,进阶,ybtoj,ll,21282,include,集合,toA
来源: https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21282.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有