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;
}