CF1886E I Wanna be the Team Leader
传送门
需要先注意到,将员工按照能力值从大到小排序后,分配给每个项目的员工一定是连续的。
再利用这个性质,设\(dp_S\)表示完成\(S\)集合的项目所需最少员工为多少个。并记录每个集合是由哪一个子集转移过来的,最后输出方案数
启示:需先观察性质,才能设计dp及状态转移方程
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e18 + 10;
int n,m,b[N];
int g[21][N];
struct node {
int w,id;
bool operator < (const node &a) const {
return w > a.w;
}
}a[N];
void solve() {
cin >> n >> m;
for(int i = 1;i <= n;i++) cin >> a[i].w,a[i].id = i;
for(int i = 1;i <= m;i++) cin >> b[i];
sort(a + 1,a + 1 + n);
for(int i = 1;i <= m;i++) {
int r = 1;
for(int j = 1;j <= n;j++) {
r = max(r,j);
while(r <= n && a[r].w < (int)ceil(b[i] * 1.0 / (r - j + 1))) r++;
g[i][j] = r;
}
}
vector<int> dp(1 << m + 1,inf);
vector<int> pre(1 << m + 1);
dp[0] = 0;
for(int i = 1;i < (1 << m);i++) {
for(int j = 0;j < m;j++) {
if(!(i & (1 << j))) continue;
if(dp[i ^ (1 << j)] >= n) continue;
if(g[j + 1][dp[i ^ (1 << j)] + 1] < dp[i]) {
dp[i] = g[j + 1][dp[i ^ (1 << j)] + 1];
pre[i] = j; //记录方案,当前状态从谁转移过来
}
}
}
int tmp = (1 << m) - 1;
if(dp[tmp] > n) return (void)(cout << "NO\n");
cout << "YES\n";
vector<vector<int> > ans(m + 10);
for(int i = m;i >= 1;i--) {
for(int j = dp[tmp];j > dp[tmp ^ (1 << pre[tmp])];j--)
ans[pre[tmp] + 1].push_back(a[j].id);
tmp = tmp ^ (1 << pre[tmp]);
}
for(int i = 1;i <= m;i++) {
cout << ans[i].size() << ' ';
for(auto j : ans[i]) cout << j << ' ';
cout << '\n';
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t = 1;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号