CF GYM 104020 G

发布时间 2023-11-01 19:06:32作者: SFlyer

link

首先,因为 \(w_i\le 10^6\),有点大,所以我们想方设法把他变小一点。

设一个快为 \(w_i=k\times x+r\)。其实,如果我们把他分为 \(x\) 个大小为 \(k\) 的块,然后一个大小为 \(r\) 的块是最优的。因为切成其他的大小的块,我们可以调整成这种切法,答案不是更劣。比如说,有 9 3 4 这三个快,\(k=8\)。你可以使 \(9=4+5\),然后 \(3,5\)\(4,4\) 配对。但是你也可以 \(9=8+1\),然后 \(3,4,1\)\(8\) 配对。

然后我们就把 \(w_i\) 弄成了 \(<k\) 的大小。(其实就是模 \(k\))。注意这个切也要算入答案。

现在考虑另一件事情。你有若干个 \(<k\) 大小的块,计个数为 \(cnt_1,cnt_2,\cdots cnt_{k-1}\)。对于 \(1\le i < \frac{k}{2}\),我们可以使 \(i\)\(k-i\) 配对,即对于 \(1\le i < \frac{k}{2}\)\(cnt_{i}\)\(cnt_{k-i}\) 均减 \(\min(cnt_i,cnt_{k-i})\),不需要任何切。发现这样切完以后,\(cnt_i>0\)\(i\) 最多有 \(4\) 个(\(k=8\) 的情况),即 \(1\)\(7\)\(2\)\(6\)\(3\)\(5\)\(4\)。设这些非零的数为 \(b_0\sim b_3\)。如果小于四个,\(b_i\) 就是 \(0\)

考虑,这些块的个数均小于 \(n\),也就是 \(100\)。实际上,个数的组合最多有 \(25^4\) 种。这时候就可以 dp 了。设 \(dp_{i,j,k,l}\) 代表用 \(i\)\(b_0\)\(j\)\(b_1\)\(k\)\(b_2\)\(l\)\(b_3\) 的,的什么呢?

这时候就要进行一个题目的转化。我们不难发现,原来的题目等同于:

给你一些 \(<k\) 大小的方块。设他们的和为 \(a\times k\)。现在,把他们分成若干组,设为 \(b\) 组,使得每一组的大小和都是 \(k\) 的倍数。最大化 \(a-b\)

这样为什么是对的呢?不切的情况下,显然 \(a=b\)。如果你切了一下,那就一定要和另一个(或多个)方块组合,就会少掉一组,即 \(b=b-1\)

所以,dp 就是最多能分出的组的个数。转移很显然。

复杂度 \(\mathcal{O}(n^4)\),但其实很松。

Code
#include <bits/stdc++.h>

using namespace std;

#define de(x) cout<<#x<<"="<<x<<endl

using ll = long long;

int n,K,a[111],ans;
int cnt[8],sum,id[4],c[4]; 

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n>>K;
	for (int i=0; i<n; i++){
		cin>>a[i];
		ans+=a[i]/K-(a[i]%K==0);
		cnt[a[i]%K]++;
	}
	for (int i=1; i<K/2; i++){
		int mnu=min(cnt[i],cnt[K-i]);
		cnt[i]-=mnu;
		cnt[K-i]-=mnu;
	}
	int tot=0;
	for (int i=1; i<K; i++){
		if (cnt[i]){
			id[tot++]=i;
			c[tot-1]=cnt[i];
			sum+=i*cnt[i];
		}
	}
	ans+=sum/K;
	int dp[c[0]+1][c[1]+1][c[2]+1][c[3]+1];
	for (int i=0; i<c[0]+1; i++){
		for (int j=0; j<c[1]+1; j++){
			for (int k=0; k<c[2]+1; k++){
				for (int l=0; l<c[3]+1; l++){
					auto &pd=dp[i][j][k][l];
					pd=0;
					if (i){
						pd=max(pd,dp[i-1][j][k][l]);
					}
					if (j){
						pd=max(pd,dp[i][j-1][k][l]);
					}
					if (k){
						pd=max(pd,dp[i][j][k-1][l]);
					}
					if (l){
						pd=max(pd,dp[i][j][k][l-1]);
					}
					if ((i*id[0]+j*id[1]+k*id[2]+l*id[3])%K==0){
						pd++;
					}
				}
			}
		}
	}
	ans-=dp[c[0]][c[1]][c[2]][c[3]]-1;
	cout<<ans<<endl;
	return 0;
}

// don't waste time!!!