【题解】CF2229F

0

\(link\)

1:转化

可以发现,将最大值最后放一定最优,此时问题转化为分配其余 \(n-1\) 个点,使得他们最小值最大

2:二分

不过这样还是很难刻画状态,注意到最小值最大,提示二分

我们二分最小值,转为判定问题

3:O(3^n)

\(dp[S]:\) 集合 \(S\) 最多可以填满多少个桶

集合枚举,发现 \(O(3^n)TLE\)

4:O(2^n)

对于一个状态,发现其填桶构造分为两部分,一部分是填满了多少个桶,另外一个是未填满的一个桶,现在 \(a\) 之和是多少

刻画状态,表示二元值

  • \(dp[S].x:\) 用状态 \(S\) ,填满组数最大值

  • \(dp[S].y:\) 用状态 \(S\) ,零碎桶 \(a\) 之和

5:构造

我们上面的思路尚未考虑题目要求的构造方式,不过问题不大,显然可以构造出来

因为每个桶,减去桶中任意一个 \(a\) ,都会 \(<=mid\) ,所以可以构造

6:代码

// Problem: F. Load Unbalancing
// Contest: Codeforces - Spectral::Cup 2026 Round 2 (Codeforces Round 1100, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/2229/problem/F
// Memory Limit: 256 MB
// Time Limit: 1500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define int long long
#define Pair pair<int,int>
#define x first
#define y second
using namespace std;
const int N=20;
int a[N];
Pair dp[1<<N];
int n,m;
bool check(int mid){
	//dp[S].x:用状态S,填满组数最大值
	//dp[S].y:用状态S,零碎之和
	for(int S=0;S<(1<<n);S++) dp[S]={0,0};
	for(int S=0;S<(1<<n);S++){
		for(int j=0;j<n;j++){
			if(S>>j&1) continue;
			int T=S^(1<<j);
			Pair cur=dp[S];
			cur.y+=a[j];
			if(cur.y>=mid) cur={cur.x+1,0};
			dp[T]=max(dp[T],cur);
		}
	}
	int ans=0;
	for(int S=0;S<(1<<n);S++) ans=max(ans,dp[S].x);
	return ans>=m;
}
void solve(){
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n);
	int add=a[n-1];n--;
	int l=0,r=add*n;
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	cout<<l+add<<"\n";
}
signed main(){
	int t;cin>>t;
	while(t--) solve();
	return 0;
}

7:历程

注意到数据范围,我想到状压,然后尝试刻画状态,发现根本刻画不出来

其实这道题只要首先注意到扔掉最大 \(a\),然后化成一个二分+状压问题

然后再注意到性质,从状态定义角度优化dp就可以做出来了

8:反思与评价

8.1

看到数据范围,可以想到状压,但是如果刻画不出来,可以搭配其他算法转化问题

一道状压题不一定是纯状压

8.2

一个TLE的状压,可以观察其性质,重新定义dp进行优化

8.3

一定要先分析性质,再dp设计

posted @ 2026-06-02 19:28  Aistyr  阅读(9)  评论(0)    收藏  举报