连锁商店 (2021CCPC女生赛)
2022/8/1 23:24:16
本文主要是介绍连锁商店 (2021CCPC女生赛),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Problem - C - Codeforces
题意
有 n ( n <= 36) 个点,每个点有颜色,每个颜色都相同的权值(为正数);有 m 条边,u -> v 且 u < v, 求从 1 号点到 i 号点的路径上,选颜色互不相同的一些点,使权值和最大
状压dp
首先可考虑 TSP 问题类似的状压dp方法,但 \(36*2^{36}\) 太大,考虑优化
可以分析一下dp需要存哪些状态:TSP 中要存已经走过的点集和当前点,因为要满足不走重复点,所以这些状态是必要的;而本题中边都满足 u < v, 不会走重复点,所以不必要存下所有走过的点集,对之后的决策有影响的就是走了哪些颜色
但是存走了哪些颜色,也是 \(2^{36}\) 级别的,但是显然如果一个颜色在所有点中只出现了一次,那这个颜色是一定可以选的,因此遇到这个颜色直接选就好,它也不会对之后的选择造成影响,所以只出现一次的颜色是不必存下来的
只存下出现 2 次及以上的颜色,这样复杂度就变成了 \(n^2*2^{\frac n2}\), 就可以状压dp了
#include <iostream> #include <algorithm> #include <cstring> #include <vector> #include <cmath> using namespace std; #define endl "\n" typedef long long ll; typedef pair<int, int> PII; const int N = 40, M = 18; int n, m, tot; int c[N], w[N], ww[N], cnt[N], id[N]; int f[(1 << M) + 10][N]; int ans[N]; vector<vector<int> > G(N); void add(int u, int v) { G[u].push_back(v); } int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> c[i]; cnt[c[i]]++; } for (int i = 1; i <= n; i++) { if (cnt[i] > 1) id[i] = tot++; else id[i] = -1; } for (int i = 1; i <= n; i++) cin >> ww[i]; for (int i = 1; i <= n; i++) w[i] = ww[c[i]]; for (int i = 1; i <= n; i++) c[i] = id[c[i]]; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; add(u, v); } memset(f, -1, sizeof f); f[0][1] = 0; for (int u = 1; u <= n; u++) { for (int s = 0; s < 1 << tot; s++) { if (f[s][u] == -1) continue; //更新自身,选还是不选 if (c[u] == -1) f[s][u] += w[u]; else { if (!(s >> c[u] & 1)) f[s | (1 << c[u])][u] = max(f[s | (1 << c[u])][u], f[s][u] + w[u]); } ans[u] = max(ans[u], f[s][u]); //转移 for (auto v : G[u]) f[s][v] = max(f[s][v], f[s][u]); } } for (int i = 1; i <= n; i++) cout << ans[i] << endl; return 0; }
floyd + dfs
本题有个性质是每个颜色的权值是一样的,所以可以认为第一次遇到某个颜色的时候就选择,所以可以 dfs(若每个颜色的权值不一定相同就只能按上述的状压dp)
首先先用 floyd 优化一下边(加速 dfs)
若存在 i -> k, k -> j, i -> j 三条边,明显多经过几个点会更优,所以只需要保留 i -> k -> j 一条路径即可,可删去 i -> j 这条边 (由于边数很小,可以压缩为二进制,也可以用邻接矩阵、 set 来存边删边)
优化完复杂度我也不会算。。。据说 dfs 是 O(n)
#include <iostream> #include <algorithm> #include <cstring> #include <vector> #include <cmath> using namespace std; #define endl "\n" typedef long long ll; typedef pair<int, int> PII; ll n, m; ll c[50], w[50], vis[50]; ll dp[50], path[50], ans[50]; void dfs(ll now, ll fa) { if (!vis[c[now]]) dp[now] = dp[fa] + w[c[now]]; else dp[now] = dp[fa]; ans[now] = max(ans[now], dp[now]); vis[c[now]]++; for (int i = 1; i <= n; i++) { if (path[now] & ((ll)1 << i)) { dfs(i, now); vis[c[i]]--; } } } void solve() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> c[i]; for (int i = 1; i <= n; i++) cin >> w[i]; for (int i = 1; i <= m; i++) { ll u, v; cin >> u >> v; path[u] = path[u] | ((ll)1 << v); } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) for (int k = 1; k <= j; k++) if (path[i] & ((ll)1 << k) && path[k] & ((ll)1 << j) && path[i] & ((ll)1 << j)) path[i] -= (ll)1 << j; dfs(1,0); for (int i = 1; i <= n; i++) cout << ans[i] << endl; } int main() { solve(); return 0; }
这篇关于连锁商店 (2021CCPC女生赛)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享