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

原题链接:https://codeforces.ml/gym/103389/problem/C

题意

有n个景点,下标代表高度,接着又m条路线,一定从低到高,每个景点都属于不同公司,每个公司都有不同的红包政策,但每个公司的红包只能领一个,问从1到 [ 1 , n ] [1,n] [1,n]所有景点能领到的最多红包金额是多少。

分析

乍一看这题的n是36,好像没法状压,其实仔细看可以发现,如果一个公司只有一个景点,那么可以直接拿红包,如果一个公司有超过一个景点,那么可以考虑进状态里,也不会超过18个状态。

这样题目就简单很多了,我们直接从小到大去转移状态, O ( N 2 ) O(N^2) O(N2)枚举两点, O ( 2 18 ) O(2^{18}) O(218)枚举状态,总复杂度在 O ( N 2 2 N 2 ) O(N^{2}2^{\frac{N}{2}}) O(N222N)。在转移状态的时候分类讨论一下就可以了。注意第一个点初始化时也要分类讨论。

Code

//
// Created by kaka0320 on 2021/11/1.
//
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
typedef pair<ll, ll> pii;
int c[50], w[50], vis[50], id[50], rnk[50];
vector<int> ve[50];
int dp[50][1<<18];
int g[50][50];
void solve() {
    int n, m; cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> c[i], ve[c[i]].push_back(i);
    for (int i = 1; i <= n; i++) cin >> w[i];
    for (int i = 1; i <= m; i++) {
        int u, v; cin >> u >> v;
        g[u][v] = 1;
    }
    int num = 0;
    for (int i = 1; i <= n; i++) {
        if (ve[i].empty()) continue;
        if (ve[i].size() == 1) vis[ve[i][0]] = 1;//1类集合,可全部取
        else {
            id[num] = i;
            rnk[i] = num;
            num++;
        }
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                if (i != j) g[i][j] |= (g[i][k] & g[k][j]);
            }
        }
    }
    if (vis[1]) dp[1][0] = w[c[1]];
    else dp[1][1<<rnk[c[1]]] = w[c[1]];
    for (int u = 1; u <= n; u++) {
        for (int st = 0; st < 1 << num; st++) {
            for (int v = 1; v <= n; v++) {
                if (!g[u][v]) continue;
                if (vis[v]) {
                    dp[v][st] = max(dp[v][st], dp[u][st] + w[c[v]]);
                } else {
                    if (!(st >> rnk[c[v]] & 1)) {
                        dp[v][st | (1 << rnk[c[v]])] = max(dp[v][st | (1 << rnk[c[v]])], dp[u][st] + w[c[v]]);
                    }
                }
            }

        }
    }
    for (int i = 1; i <= n; i++) {
        int ma = 0;
        for (int st = 0; st < 1 << num; st++) ma = max(ma, dp[i][st]);
        cout << ma << endl;
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
#endif

#ifdef ACM_LOCAL
    auto start = clock();
#endif
    int t = 1;
//    cin >> t;
    while(t--) solve();
#ifdef ACM_LOCAL
    auto end = clock();
    cerr << "Run Time: " << double(end-start) / CLOCKS_PER_SEC << "s" << endl;
#endif
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值