连锁商店 (2021CCPC女生赛)
作者:互联网
题意
有 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;
}
标签:now,女生,int,ll,50,2021CCPC,连锁商店,include,dp 来源: https://www.cnblogs.com/hzy717zsy/p/16541417.html