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;
}
posted @ 2025-08-15 11:02  孤枕  阅读(8)  评论(0)    收藏  举报