題意:
分析:
記 \(S_{i}\) 表示目前第 \(i\) 個集合裡的元素個數。
集合之間互不區分,強制欽定必須滿足 \(S_{i} \le S_{i+1}(i<k)\)。
經搜尋發現,這樣的狀態數量最多約為 \(1.8 \times 10^5\)。
極差可以這樣處理:將 \(a\) 排序,\(S_{i}\) 第一次加入某個元素 \(x\),則貢獻加上 \(-x\);\(S_{i}\) 被 \(y\) 填滿,則貢獻加上 \(+y\)。
這樣看起來可以 dp,但還有一個問題:如何處理每個集合的元素必須互不相同呢?
可以強制要求所有相同的元素必須以一個固定的順序加入,使得其在不同的集合,可以從大到小加或從小到大加(元素個數)。這裡採用前者。
f[S][j]表示(從小往大填)已經填了i(∑S[i])個數,第i個數在加入後它目前在的集合的大小為j
當我們填入a[i+1]時,如果a[i+1]=a[i],則需要保證a[i+1]填入的集合的大小小於j即可
然後做完了!
時隔 \(7\) 個月,以前的我看不懂題解,但現在我 AC 了,牛不牛。
#include<bits/stdc++.h>
#define N 110
#define int long long
#define base 131
using namespace std;
bool s1;
int n, k, len;
int a[N], now[N];
vector<int>z[110];
int S[200005][110];
int tot;
unordered_map<int, int>id;
void dfs(int x, int lst) {
if(x > k) {
tot++;
int g = 0;
int num = 0;
for(int i = 1; i <= k; i++) {
S[tot][i] = now[i];
g += now[i];
num = num * base + now[i];
}
id[num] = tot;
z[g].push_back(tot);
//cout << tot << endl;
return;
}
for(int i = lst; i <= len; i++) {
now[x] = i;
dfs(x + 1, i);
}
}
int f[200005][110];
//f[S][j]表示(從小往大填)已經填了i(∑S[i])個數,第i個數在加入後它目前在的集合的大小為j
//當我們填入a[i+1]時,如果a[i+1]=a[i],則需要保證a[i+1]填入的集合的大小小於j即可
void upd(int &x, int y) {
x = min(x, y);
}
bool s2;
signed main() {
//freopen("diyiti06.in", "r", stdin);
//ios::sync_with_stdio(0);
//cin.tie(0); cout.tie(0);
//cerr << (&s1 - &s2) / 1024 / 1024 << " MB\n";
cin >> n >> k;
len = n / k;
for(int i = 1; i <= n; i++) cin >> a[i];
/*if(k == n) {
cout << 0;
return 0;
}*/
sort(a + 1, a + n + 1);
dfs(1, 0);
//cerr << "e";
for(int S = 1; S <= tot; S++)
for(int j = 0; j <= len; j++) f[S][j] = 1e18;
/*for(int s = 1; s <= tot; s++) {
cout << "S " << s << " : ";
for(int j = 1; j <= len; j++) cout << S[s][j] << " ";
cout << endl;
}
for(int i = 1; i <= n; i++) {
cout << i << " : ";
for(auto x : z[i]) cout << x << " ";
cout << endl;
}*/
f[2][1] = -a[1];
if(len == 1) f[2][1] = 0;
for(int i = 1; i < n; i++) {
for(auto s : z[i]) {
//if(i == 1) cout << "look : " << s << endl;
for(int x = 1; x <= k; x++) {
if(S[s][x]) {
//if(i == 1) cout << "ppp : " << x << endl;
int j = S[s][x]; //第i個數加入的是第x個集合
if(f[s][j] == 1e18) continue;
//printf("f[%lld][%lld] = %lld\n", s, j, f[s][j]);
//考慮填入a[i+1]
for(int y = 1; y <= k; y++) { //將a[i+1]放入第y個集合裡
if(a[i] == a[i + 1] && S[s][y] >= j) break;
for(int h = 1; h <= k; h++) now[h] = S[s][h];
now[y]++;
if(now[y] > len) continue;
int J = now[y], derta = 0;
if(J == len) derta += a[i + 1];
if(J == 1) derta -= a[i + 1];
sort(now + 1, now + k + 1);
int num = 0;
for(int h = 1; h <= k; h++)
num = num * base + now[h];
int Nxt_S = id[num];
upd(f[Nxt_S][J], f[s][j] + derta);
}
}
}
}
}
if(f[tot][len] == 1e18) cout << -1;
else cout << f[tot][len];
return 0;
}
/*
6 3
1 1 1 1 2 5
*/