Loading

CF1886E I Wanna be the Team Leader (贪心+状压 dp)

CF1886E I Wanna be the Team Leader

贪心+状压 dp

注意到每个项目的程序员水平都要大于等于要求值,那么就相当于限制只与程序员最小值有关。

那么考虑将 \(a\) 序列从小到大排序,那么就有结论:每个项目的程序员都是一段连续的区间。考虑贪心去证这个结论,假如有一段同一个项目的程序员不是连续的,那么将他们拼在一起并将其中其他项目的程序员往后堆,一定不劣,因为不影响当前项目的最小值,还让其他项目的最小值更大。

考虑状压 dp。跟 CF1550E Stringforces 的状态表示一样,预处理也一样。设 \(f_s\) 表示完成项目集合为 \(s\) 的最短前缀。预处理 \(g_{i,j}\) 表示从 \(j\) 位置开始,满足项目 \(i\) 的最小右端点。那么转移就是

\[f_{s|2^i}=\min g_{i,f_s+1} \]

难点在方案的输出,记录转移点,利用 \(g\) 数组找到每一次选择的区间。

那么就做完了。复杂度 \(O(m2^m)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10, M = 21;
int n, m, lim;
pii a[N], ans[M];
int b[M];
int g[M][N], f[1 << M], pos[1 << M];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> m;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i].fi;
		a[i].se = i;
	}
	for(int i = 0; i < m; i++) {
		std::cin >> b[i];
	}
	std::sort(a + 1, a + n + 1, [&](pii a, pii b) {
		return a.fi < b.fi;
	});

	for(int i = 0; i < m; i++) {
		g[i][n + 1] = n + 1;
		for(int j = n; j >= 1; j--) {
			g[i][j] = std::min(g[i][j + 1], std::min(n + 1, j + ((b[i] + a[j].fi - 1) / a[j].fi) - 1));
		}
	}

	lim = (1 << m) - 1;
	for(int s = 0; s <= lim; s++) f[s] = n + 1;
	f[0] = 0;
	for(int s = 0; s < lim; s++) {
		if(f[s] > n) continue;
		for(int i = 0; i < m; i++) {
			if(!(s & (1 << i))) {
				if(f[s | (1 << i)] > g[i][f[s] + 1]) {
					f[s | (1 << i)] = g[i][f[s] + 1];
					pos[s | (1 << i)] = i;
				}
			}
		}
	}

	if(f[lim] > n) {
		std::cout << "NO\n";
		return 0;
	}

	while(lim) {
		for(int i = f[lim ^ (1 << pos[lim])] + 1; i <= f[lim]; i++) {
			if(g[pos[lim]][i] != g[pos[lim]][i + 1]) {
				ans[pos[lim]] = {i, f[lim]};
				break;
			}
		}
		lim = lim ^ (1 << pos[lim]);
	}

	std::cout << "YES\n";
	for(int i = 0; i < m; i++) {
		std::cout << ans[i].se - ans[i].fi + 1 << " ";
		for(int j = ans[i].fi; j <= ans[i].se; j++) {
			std::cout << a[j].se << " \n"[j == ans[i].se];
		}
	}
	return 0;
}
posted @ 2024-06-07 00:34  Fire_Raku  阅读(12)  评论(0编辑  收藏  举报