YC312A [ 20240702 CQYC省选模拟赛 T1 ] 第一题(diyiti)

题意

给定一个长度为 \(n\) 的可重集,以及正整数 \(k\)

设一个子集的价值为子集中最大值减去最小值,你需要将这个可重集划分为 \(k\) 个子集,使得价值之和最小,子集需要满足不重。

\(n, k \le 100\)

Sol

思考一下发现如果不记录每个子集的信息是不好做的。

考虑将所有子集的大小记录下来,发现这个的数量是 \((0, 0)\) 走到 \((k, n / k)\) 的方案数,显然为 \(C(n / k + k, n / k)\)。当 \(k\)\(\sqrt n\) 时有最大值 \(C(2 \sqrt n, \sqrt n)\)

简单跑一下发现这个东西只有 \(1.8 \times 10 ^ 5\)

上个暴力 \(\text{dp}\),设 \(f_{i, j, S}\) 表示前 \(i\) 小个数,第 \(i\) 个数放在大小为 \(j\) 的集合之中,每个子集的大小集合为 \(S\)

转移显然,若 \(s_i = s_{i = 1}\) 保证当前转移的 \(j \le j'\) 即可,因为之前放的相同的元素的集合一定是 \(\ge j + 1\) 的。

时间复杂度 \(O(nk\times C(2 \sqrt n, \sqrt n))\),具体实现中可能需要使用 \(\text{map}\)\(\text{vector}\) 的编号,这样会挂 \(\log\),可能过不去。

可以考虑交换一下枚举顺序,把保存上一个状态的 \(\text{vector}\) 拆开,合并处理 相同的 \(S\),这样 \(\log\) 就在外面了。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <array>
#include <vector>
#define int long long
#define pii pair <int, int>
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
	int p = 0, flg = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') flg = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		p = p * 10 + c - '0';
		c = getchar();
	}
	return p * flg;
}
void write(int x) {
	if (x < 0) {
		x = -x;
		putchar('-');
	}
	if (x > 9) {
		write(x / 10);
	}
	putchar(x % 10 + '0');
}
bool _stmer;

#define fi first
#define se second

const int N = 2e5 + 5, M = 105, inf = 1e12;

map <vector <int>, int> mp;
array <vector <int>, N> idx;
vector <int> isl;

int cnt = 0, tot = 0;

void dfs(int step, int n, int k) {
	tot++;
	if (step == k) {
		cnt++;
		mp[isl] = cnt, idx[cnt] = isl;
		return;
	}
	int st = isl.size() ? isl.back() : 0;
	for (int i = st; i <= n / k; i++)
		isl.push_back(i), dfs(step + 1, n, k), isl.pop_back();
}

array <array <array <int, N>, M>, 2> f;
array <int, M> s;

bool _edmer;
signed main() {
	cerr << (&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
	int n = read(), k = read();
	vector <int> tp;
	for (int i = 1; i <= n; i++) s[i] = read(), tp.push_back(s[i]);
	sort(s.begin() + 1, s.begin() + 1 + n);
	sort(tp.begin(), tp.end()), tp.erase(unique(tp.begin(), tp.end()), tp.end());
	dfs(0, n, k);
	int pl = 1;
	map <int, vector <int> > q, _q;
	q[1].push_back(0);
	for (int i = 0; i < M; i++)
		f[0][i].fill(inf), f[1][i].fill(inf);
	f[0][0][1] = 0;
	for (int i = 1; i <= n; i++) {
		for (auto S : q) {
			vector <int> res = idx[S.fi];
			for (int j = 1; j <= k; j++) {
				for (auto p : S.se) {
					if (s[i] == s[i - 1] && res[j - 1] > p)
						continue;
					if (j < k && res[j - 1] == res[j]) continue;
					if (res[j - 1] == n / k) continue;
					res[j - 1]++;
					int now = 0, jl = res[j - 1] - 1;
					if (res[j - 1] == 1) now += -s[i];
					if (res[j - 1] == n / k) now += s[i];
					if (f[pl][jl][mp[res]] >
						f[pl ^ 1][p][S.fi] + now) {
						_q[mp[res]].push_back(jl);
						f[pl][jl][mp[res]] =
							f[pl ^ 1][p][S.fi] + now;
					}
					res[j - 1]--;
				}
			}
		}
		pl ^= 1, q = _q, _q.clear();
	}
	int ans = inf;
	for (int i = 0; i <= n / k; i++)
		ans = min(ans, f[pl ^ 1][i][cnt]);
	if (ans == inf)
		puts("-1");
	else write(ans), puts("");
	return 0;
}

题意

给定一个长度为 \(n\) 的可重集,以及正整数 \(k\)

设一个子集的价值为子集中最大值减去最小值,你需要将这个可重集划分为 \(k\) 个子集,使得价值之和最小,子集需要满足不重。

\(n, k \le 100\)

Sol

思考一下发现如果不记录每个子集的信息是不好做的。

考虑将所有子集的大小记录下来,发现这个的数量是 \((0, 0)\) 走到 \((k, n / k)\) 的方案数,显然为 \(C(n / k + k, n / k)\)。当 \(k\)\(\sqrt n\) 时有最大值 \(C(2 \sqrt n, \sqrt n)\)

简单跑一下发现这个东西只有 \(1.8 \times 10 ^ 5\)

上个暴力 \(\text{dp}\),设 \(f_{i, j, S}\) 表示前 \(i\) 小个数,第 \(i\) 个数放在大小为 \(j\) 的集合之中,每个子集的大小集合为 \(S\)

转移显然,若 \(s_i = s_{i = 1}\) 保证当前转移的 \(j \le j'\) 即可,因为之前放的相同的元素的集合一定是 \(\ge j + 1\) 的。

时间复杂度 \(O(nk\times C(2 \sqrt n, \sqrt n))\),具体实现中可能需要使用 \(\text{map}\)\(\text{vector}\) 的编号,这样会挂 \(\log\),可能过不去。

可以考虑交换一下枚举顺序,把保存上一个状态的 \(\text{vector}\) 拆开,合并处理 相同的 \(S\),这样 \(\log\) 就在外面了。

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <array>
#include <vector>
#define int long long
#define pii pair <int, int>
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
	int p = 0, flg = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') flg = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		p = p * 10 + c - '0';
		c = getchar();
	}
	return p * flg;
}
void write(int x) {
	if (x < 0) {
		x = -x;
		putchar('-');
	}
	if (x > 9) {
		write(x / 10);
	}
	putchar(x % 10 + '0');
}
bool _stmer;

#define fi first
#define se second

const int N = 2e5 + 5, M = 105, inf = 1e12;

map <vector <int>, int> mp;
array <vector <int>, N> idx;
vector <int> isl;

int cnt = 0, tot = 0;

void dfs(int step, int n, int k) {
	tot++;
	if (step == k) {
		cnt++;
		mp[isl] = cnt, idx[cnt] = isl;
		return;
	}
	int st = isl.size() ? isl.back() : 0;
	for (int i = st; i <= n / k; i++)
		isl.push_back(i), dfs(step + 1, n, k), isl.pop_back();
}

array <array <array <int, N>, M>, 2> f;
array <int, M> s;

bool _edmer;
signed main() {
	cerr << (&_stmer - &_edmer) / 1024.0 / 1024.0 << "MB\n";
	int n = read(), k = read();
	vector <int> tp;
	for (int i = 1; i <= n; i++) s[i] = read(), tp.push_back(s[i]);
	sort(s.begin() + 1, s.begin() + 1 + n);
	sort(tp.begin(), tp.end()), tp.erase(unique(tp.begin(), tp.end()), tp.end());
	dfs(0, n, k);
	int pl = 1;
	map <int, vector <int> > q, _q;
	q[1].push_back(0);
	for (int i = 0; i < M; i++)
		f[0][i].fill(inf), f[1][i].fill(inf);
	f[0][0][1] = 0;
	for (int i = 1; i <= n; i++) {
		for (auto S : q) {
			vector <int> res = idx[S.fi];
			for (int j = 1; j <= k; j++) {
				for (auto p : S.se) {
					if (s[i] == s[i - 1] && res[j - 1] > p)
						continue;
					if (j < k && res[j - 1] == res[j]) continue;
					if (res[j - 1] == n / k) continue;
					res[j - 1]++;
					int now = 0, jl = res[j - 1] - 1;
					if (res[j - 1] == 1) now += -s[i];
					if (res[j - 1] == n / k) now += s[i];
					if (f[pl][jl][mp[res]] >
						f[pl ^ 1][p][S.fi] + now) {
						_q[mp[res]].push_back(jl);
						f[pl][jl][mp[res]] =
							f[pl ^ 1][p][S.fi] + now;
					}
					res[j - 1]--;
				}
			}
		}
		pl ^= 1, q = _q, _q.clear();
	}
	int ans = inf;
	for (int i = 0; i <= n / k; i++)
		ans = min(ans, f[pl ^ 1][i][cnt]);
	if (ans == inf)
		puts("-1");
	else write(ans), puts("");
	return 0;
}

posted @ 2024-07-10 22:03  cxqghzj  阅读(12)  评论(0编辑  收藏  举报