专栏文章

题解:P13494 【MX-X14-T4】分门别类

P13494题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@miookbx5
此快照首次捕获于
2025/12/02 22:35
3 个月前
此快照最后确认于
2025/12/02 22:35
3 个月前
查看原文

题外话

这题看起来很像贪心啊,但由于本人贪心太废了,所以只好使用二分加动态规划来做了。

思路

先把相同的数缩成一个数,将原本长度为 nnaa 数组变为长度为 mm 的互不相同的 aa 数组,用 bib_i 记录 ii 的出现次数。观察到答案具有单调性,考虑二分答案 xx。问题关键在于如何判断划分出的集合数量能不能为 xx。我们枚举每一个 aia_i,用 dpi,jdp_{i,j} 表示前 ii 个数,能不能划分出 jj 个大小为奇数的集合,若能 dpi,jdp_{i,j}11,否则为 00。则最后 dpm,0dp_{m,0} 就是答案。
考虑转移,对于 dpi,jdp_{i,j},我们枚举 kk 表示使用了 kkaia_i 放入了之前的 kk 个大小为奇数的集合,剩余的 (baik)(b_{a_i}-k)aia_i 放入了之前的 (baik)(b_{a_i}-k) 个大小为偶数的集合,其余集合的奇偶性不变。既然放一个数字集合奇偶性会取反,那么 dpi,jdp_{i,j} 就是可以从 dpi1,j+2×kbaidp_{i-1,j+2\times k-b_{a{i}}} 转移过来的,取最大值即可。
求出最小值后我们还要构造一种方案,此时只需要用 fi,jf_{i,j} 记录 dpi,jdp_{i,j} 是从哪里转移过来的即可。
由于循环前两维总和为 nn,总时间复杂度 O(n2logn)O(n^2\log n)

代码

CPP
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,a[1005],b[1000005],c[1005],dp[1005][1005],f[1005][1005];
vector<int> v[1005];
bool check(int x) {
	memset(dp,0,sizeof(dp));
	dp[1][b[a[1]]]=1;
	for(int i=2; i<=m; i++) {
		if(b[a[i]]>x)return 0;
		for(int j=0; j<=x; j++) {
			for(int k=0; k<=b[a[i]]; k++) {
				if(j+k-b[a[i]]>=0&&x-j-k>=0)dp[i][j]=max(dp[i][j],dp[i-1][j+2*k-b[a[i]]]);
			}
		}
	}
	return dp[m][0];
}
bool cmp(vector<int> x,vector<int> y){
	return x.size()%2>y.size()%2;
}
void gx(int x) {
	memset(dp,0,sizeof(dp));
	dp[1][b[a[1]]]=1;
	f[1][b[a[1]]]=0;
	for(int i=2; i<=m; i++) {
		for(int j=0; j<=x; j++) {
			for(int k=0; k<=b[a[i]]; k++) {
				if(j+k-b[a[i]]>=0&&x-j-k>=0) {
					if(dp[i-1][j+2*k-b[a[i]]]) {
						dp[i][j]=1;
						f[i][j]=k;
						break;
					}
				}
			}
		}
	}
	int j=0;
	for(int i=m; i>=1; i--) {
		c[i]=f[i][j];
		j+=2*f[i][j]-b[a[i]];
	}
	for(int i=1; i<=m; i++) {
		int y=1;
		for(int j=1; j<=b[a[i]]; j++) {
			if(v[y].size()%2==1) {
				if(c[i]) {
					c[i]--;
					v[y].push_back(a[i]),y++;
				}
				else{
					while(v[y].size()%2==1)y++;
					v[y].push_back(a[i]),y++;
				}
			} else v[y].push_back(a[i]),y++;
		}
		sort(v+1,v+x+1,cmp);
	}
}
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--) {
		cin>>n;
		for(int i=1; i<=n; i++)cin>>a[i];
		if(n%2==1) {
			cout<<-1<<"\n";
			continue;
		}
		for(int i=1; i<=n; i++)b[a[i]]++;
		sort(a+1,a+n+1);
		m=unique(a+1,a+n+1)-a-1;
		int l=1,r=n/2,mid,ans=-1;
		while(l<=r) {
			mid=(l+r)/2;
			if(check(mid))ans=mid,r=mid-1;
			else l=mid+1;
		}
		cout<<ans<<"\n";
		if(ans!=-1) {
			gx(ans);
			for(int i=1; i<=ans; i++) {
                cout<<v[i].size()<<" ";
                for(int j=0;j<v[i].size();j++){
                	cout<<v[i][j]<<" ";
				}
				v[i].clear();
				cout<<"\n";
			}
		}
		for(int i=1; i<=m; i++)b[a[i]]=0;
	}
	return 0;
}

后记

贪心我们分手吧,我怕动态规划误会。

评论

2 条评论,欢迎与作者交流。

正在加载评论...