zoukankan      html  css  js  c++  java
  • E. Team Building (状压dp + 思维)

    题目:传送门

    题意:你有 n 个人,你想从这 n 个人中选 p 个人去到不同的 p 个位置, 选 k 个人作为观众。如果第 i 个人被选为观众他的贡献就是 a[ i ],如果第 i 个人被选为第 j 个位置上的人,那么他的贡献就是 b[ i ][ j ]。问你选 p 个位置上的人和 k 个观众最大的贡献是多少。

       1 <= n <= 1e5, 1 <= p <= 7, p + k <= n

    思路:

    这个题,显然是一个状压 dp, dp[ i ][ j ] = 前 i 个人,已选的位置的状态是 j 的最大贡献。 如果不需要选观众的话,这样就已经足够了。

    现在考虑观众要怎么选,我们可以对每个人作为观众时的贡献按降序排个序,即对 a 数组排序,然后,我们肯定优先选择贡献最大的前 k 个人。

    然后我们维护一个 pos[ i ] 表示 排序前的 a[ i ]排序后所在的位置的下标。

    这样的话,我们在递推的过程中,就要考虑当前这个人是不是已经被选为观众了。怎么考虑呢?

    如果,我们当前枚举到的第 i 个人,它的 pos[ i ] <= k,那么它肯定是已经被选为观众的,那我们现在想让它做为某个位置上的人,那就得取消掉它作为观众的贡献,然后再重新选一名观众咯。那我们就减掉 a[ pos[ i ] ] 然后再加上 a[ k + 1 ]。这样的话,如果下一次,我们选了 pos[ i ] = k + 1 的人作为某个位置上的人的话呢,我们就不能只是单纯的通过判断 pos[ i ] <= k 来判断他是不是作为观众。那咋整啊。

    我们可以再维护一个 c[ i ][ j ] 表示前 i 个人已选的位置的状态是 j 的最优情况,有 c[ i ][ j ] 曾经作为观众。 这样的话,我们可以通过 pos[ i ] <= k + c[ i ][ j ] 来判断他是否是观众。

    这里还有一个注意的点,就是,我们枚举 i 的时候,得按照他们作为观众时的贡献 从大到小枚举。

    如果你没有这样枚举的话,会有这么一种情况,就是,你先枚举到了 pos[ i ] = k + 1 的数,然后你判断的时候,它没有作为观众。后来,你又枚举到了 pos[ i ] = k,你发现它是观众,所以你把它作为观众的贡献减去了,然后加上了 pos[ i ] = k + 1 作为观众的贡献,但是,此时你 pos[ i ] = k + 1 已经早就被你选为某个位置上的人了。 这样就会出错,我一开始没注意到,一直wa37,后面会给出错误示范的代码。

    在这里, c[ i ][ j ] 这个数组是可以不要的,你按照 a[ i ] 从大到小枚举的时候,你的 j 的二进制位上有多少个 1 就代表着你已经选好了多少个位置上的人,我们设有 cnt 个 1 好了,那你的前 cnt + k 个人要么是观众,要么是某个位置上的人, 所以你判断他是否被选为观众,你只需要判断,它 pos[ i ] <= cnt + k 是否成立就好了。

    我的代码可能跟我说的不太一样,但是思路是差不多的,我这份代码没有优化 c[ i ][ j ]。

    #include <bits/stdc++.h>
    #define LL long long
    #define mem(i, j) memset(i, j, sizeof(i))
    #define rep(i, j, k) for(int i = j; i <= k; i++)
    #define dep(i, j, k) for(int i = k; i >= j; i--)
    #define pb push_back
    #define make make_pair
    #define INF INT_MAX
    #define inf LLONG_MAX
    #define PI acos(-1)
    #define fir first
    #define sec second
    using namespace std;
     
    const int N = 1e5 + 5;
     
    LL b[N][10];
     
    LL a[N];
    int pos[N]; /// a[i]排名后下标为 i 的在排名前的下标是 pos[i]
     
    LL dp[N][130]; 
    int c[N][130];
     
    bool cmp(int i, int j) {
        return a[i] > a[j];
    }
     
    void solve() {
     
        int n, p, K;
        scanf("%d %d %d", &n, &p, &K);
     
        rep(i, 1, n) scanf("%lld", &a[i]), pos[i] = i;
        sort(pos + 1, pos + 1 + n, cmp); /// pos[ i ] 存的是 a 数组中第 i 大的数的下标
     
        rep(i, 1, n) rep(j, 0, p - 1) scanf("%lld", &b[i][j]);
        LL res = 0;
        rep(i, 1, K) res += a[pos[i]]; /// 先把前 k 大的累加起来,这些先作为观众
     
        rep(i, 0, (1 << p) - 1) dp[0][i] = res, c[0][i] = 0;
     
     
        rep(i, 1, n) { /// 按照数组 a 降序枚举
            rep(j, 0, (1 << p) - 1) dp[i][j] = dp[i - 1][j], c[i][j] = c[i - 1][j]; /// 初始化
            rep(j, 0, (1 << p) - 1) {
                rep(k, 0, p - 1) {
                    if((j >> k) & 1) continue;
     
                    if(i <= (c[i - 1][j] + K)) { ///被选为观众
                        int id = c[i - 1][j] + 1 + K;
                        LL add = a[pos[id]] - a[pos[i]];
                        if(dp[i - 1][j] + b[pos[i]][k] + add > dp[i][j | (1 << k)]) {
                            dp[i][j | (1 << k)] = dp[i - 1][j] + b[pos[i]][k] + add;
                            c[i][j | (1 << k)] = c[i - 1][j] + 1;
                        }
                    }
                    else { /// 不作为观众
                        if(dp[i - 1][j] + b[pos[i]][k] > dp[i][j | (1 << k)]) {
                            dp[i][j | (1 << k)] = dp[i - 1][j] + b[pos[i]][k];
                            c[i][j | (1 << k)] = c[i - 1][j];
                        }
                    }
                }
            }
        }
     
        printf("%lld
    ", dp[n][(1 << p) - 1]);
     
    }
     
    int main() {
     
    //    int _; scanf("%d", &_);
    //    while(_--) solve();
     
        solve();
     
        return 0;
    }

    错误示范

    #include <bits/stdc++.h>
    #define LL long long
    #define mem(i, j) memset(i, j, sizeof(i))
    #define rep(i, j, k) for(int i = j; i <= k; i++)
    #define dep(i, j, k) for(int i = k; i >= j; i--)
    #define pb push_back
    #define make make_pair
    #define INF INT_MAX
    #define inf LLONG_MAX
    #define PI acos(-1)
    #define fir first
    #define sec second
    using namespace std;
     
    const int N = 1e5 + 5;
     
    LL b[N][10];
    bool vis[N];
    pair < LL, int > a[N];
     
    LL dp[N][130];
    int c[N][130];
    int pos[N];
    bool cmp(pair < LL, int > A, pair < LL, int > B) {
        return A.fir > B.fir;
    }
     
    void solve() {
     
        int n, p, K;
        scanf("%d %d %d", &n, &p, &K);
     
        rep(i, 1, n) scanf("%lld", &a[i].fir), a[i].sec = i;
        sort(a + 1, a + 1 + n, cmp);
        rep(i, 1, n) pos[a[i].sec] = i;
        rep(i, 1, n) rep(j, 0, p - 1) scanf("%lld", &b[i][j]);
        LL res = 0;
        rep(i, 1, K) res += a[i].fir, vis[a[i].sec] = 1;
     
        rep(i, 0, (1 << p) - 1) dp[0][i] = res, c[0][i] = 0;
     
        rep(i, 1, n) {
            rep(j, 0, (1 << p) - 1) dp[i][j] = dp[i - 1][j], c[i][j] = c[i - 1][j];
            rep(j, 0, (1 << p) - 1) {
                rep(k, 0, p - 1) {
                    if((j >> k) & 1) continue;
     
                    if(pos[i] <= c[i - 1][j] + K) {
                        int id = c[i - 1][j] + 1 + K;
    //                    cout << c[i - 1][j] << " " << K << " " << id << endl;
                        LL add = a[id].fir - a[pos[i]].fir;
    //                    cout << id << " " << pos[i] << " " << add << endl;
                        if(dp[i - 1][j] + b[i][k] + add > dp[i][j | (1 << k)]) {
                            dp[i][j | (1 << k)] = dp[i - 1][j] + b[i][k] + add;
                            c[i][j | (1 << k)] = c[i - 1][j] + 1;
                        }
                    }
                    else {
                        if(dp[i - 1][j] + b[i][k] > dp[i][j | (1 << k)]) {
                            dp[i][j | (1 << k)] = dp[i - 1][j] + b[i][k];
                            c[i][j | (1 << k)] = c[i - 1][j];
                        }
                    }
                }
            }
        }
     
        printf("%lld
    ", dp[n][(1 << p) - 1]);
     
    }
     
    int main() {
     
    //    int _; scanf("%d", &_);
    //    while(_--) solve();
     
        solve();
     
        return 0;
    }
    一步一步,永不停息
  • 相关阅读:
    头条java 后台一面凉经
    JVM运行时数据区
    基础篇——Spring Cloud Hystrix
    bug篇——idea拉取代码认证失败重新登录
    基础篇——代理模式之SpringAOP
    基础篇——代码优化100条之(11—20)
    电商项目实战(架构八)——RabbitMQ实现延迟消息
    电商项目实战(架构七)——Mongodb实现文档操作
    基础篇——代码优化100条之(1—10)
    电商项目实战(架构六)——Elasticsearch实现商品搜索
  • 原文地址:https://www.cnblogs.com/Willems/p/12420648.html
Copyright © 2011-2022 走看看