2021CCPC女生专场 C. 连锁商店 状压DP

你现在在1号位置,给出若干单向路径,给出 n n n个节点的所属公司,每个公司只能领取一次红包,问1号节点到每个节点所能领取到的累积最大金额,节点之间是独立的

  • 这是一道比较好的状压 D P DP DP练习题,有一个思维点就是 n ≤ 36 n\leq36 n36,如果直接进行状压肯定不行,但是因为如果公司只出现了一次,那么直接选择就好了;如果公司出现次数多于1次,那么这一类公司的数量不会超过18,所以我们可以对这第二类公司进行状压
  • 我们设 d p [ s ] [ i ] dp[s][i] dp[s][i]表示当前已经选择(当前还没选)的第二类公司的状态为 s s s,当前选择到第 i i i个景点所获得的最大金额, b o o k [ i ] book[i] book[i]表示第 i i i个景点属于第几个第二类公司, w [ i ] w[i] w[i]表示第 i i i个公司的红包金额, c [ i ] c[i] c[i]表示第 i i i个商店属于哪个公司
  • 首先确定初态,有两种情况,第一个景点属不属于第二类公司,如果属于第二类公司,那么有 d p [ 1 < < ( b o o k [ 1 ] − 1 ) ] [ 1 ] = w [ c [ 1 ] ] dp[1<<(book[1]-1)][1]=w[c[1]] dp[1<<(book[1]1)][1]=w[c[1]],意思就是只选这一家,其他都不选;如果不属于,那么有 d p [ 0 ] [ 1 ] = w [ c [ 1 ] ] dp[0][1]=w[c[1]] dp[0][1]=w[c[1]]
  • 解决了初态,那么现在开始状态转移,转移策略就是如果当前景点属于第二类公司,那么根据 d p dp dp方程的意义进行转移;如果不属于,那么直接选择即可,具体见代码
#include <bits/stdc++.h>

using namespace std;

int main(){
    
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, m;
    cin >> n >> m;
    vector<int> c(n + 1);
    vector<int> w(n + 1);
    vector<vector<int> > g(n + 1);
    map<int, int> mp, book;
    for(int i=1;i<=n;i++){
    
    
        cin >> c[i];
        mp[c[i]] += 1;
    }
    int st = 0;
    for(int i=1;i<=n;i++){
    
    
        if(mp[c[i]] >= 2){
    
    
            if(!book.count(c[i])){
    
    
                st += 1;
                book[c[i]] = st;
            }
        }
    }
    for(int i=1;i<=n;i++) cin >> w[i];
    while(m--){
    
    
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
    }
    st = (1 << st);
    vector<vector<int> > dp(st, vector<int> (n + 1, -1));
    vector<int> ans(n + 1);
    if(!book.count(c[1])) dp[0][1] = w[c[1]];
    else{
    
    
        dp[1 << (book[c[1]] - 1)][1] = w[c[1]];
    }
    for(int s=0;s<st;s++){
    
    
        for(int i=1;i<=n;i++){
    
    
            if(dp[s][i] == -1) continue;
            for(auto j : g[i]){
    
    
                if(!book.count(c[j])){
    
    
                    dp[s][j] = max(dp[s][j], dp[s][i] + w[c[j]]);
                    continue;
                }
                if(s & (1 << (book[c[j]] - 1))){
    
    
                    dp[s][j] = max(dp[s][j], dp[s][i]);
                }else{
    
    
                    dp[s ^ (1 << (book[c[j]] - 1))][j] = max(dp[s ^ (1 << (book[c[j]] - 1))][j], dp[s][i] + w[c[j]]);
                }
            }
            ans[i] = max(ans[i], dp[s][i]);
        }
    }
    for(int i=1;i<=n;i++){
    
    
        cout << ans[i] << '\n';
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/121399249