2021CCPC女生赛C. 连锁商店 状压DP
Posted kaka0010
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}
以上是关于2021CCPC女生赛C. 连锁商店 状压DP的主要内容,如果未能解决你的问题,请参考以下文章
2021年CCPC女生专场(淄博)C 连锁商店 (状压DP)
2021年CCPC女生专场(淄博)C 连锁商店 (状压DP)
2021年CCPC女生专场(淄博)I. 驾驶卡丁车(状压 + 模拟)