题目
算法标签: 树形 d p dp dp, T a r j a n Tarjan Tarjan算法求强连通分量, 树上背包
思路
发现可能存在互相依赖的情况, 可以使用 T a r j a n Tarjan Tarjan算法将互相依赖的视为一个点进行缩点, 然后在新的拓扑图上跑树上背包 , 创建虚拟源点, 将所有 s c c scc scc连接, 然后在此基础上进行动态规划
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110, M = 1010;
int n, m, w[N], v[N];
int head1[N], head2[N], ed[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int scc_cnt, id[N], stk[N], top;
bool in_stk[N];
int scc_w[N], scc_v[N];
int deg[N];
int f[N][M];
void add(int head[], int u, int v) {
ed[idx] = v, ne[idx] = head[u], head[u] = idx++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp;
stk[++top] = u, in_stk[u] = true;
for (int i = head1[u]; ~i; i = ne[i]) {
int v = ed[i];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (in_stk[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
++scc_cnt;
int ver;
do {
ver = stk[top--];
in_stk[ver] = false;
id[ver] = scc_cnt;
scc_w[scc_cnt] += w[ver], scc_v[scc_cnt] += v[ver];
} while (ver != u);
}
}
void dfs(int u) {
for (int j = scc_v[u]; j <= m; ++j) {
f[u][j] = scc_w[u];
}
for (int i = head2[u]; ~i; i = ne[i]) {
int v = ed[i];
dfs(v);
for (int j = m; j >= scc_v[u]; --j) {
for (int k = 0; k <= j - scc_v[u]; ++k) {
f[u][j] = max(f[u][j], f[u][j - k] + f[v][k]);
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
memset(head1, -1, sizeof head1);
memset(head2, -1, sizeof head2);
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> v[i];
for (int i = 1; i <= n; ++i) cin >> w[i];
for (int i = 1; i <= n; ++i) {
int fa;
cin >> fa;
if (fa) add(head1, fa, i);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) tarjan(i);
}
for (int u = 1; u <= n; ++u) {
for (int i = head1[u]; ~i; i = ne[i]) {
int v = ed[i];
int scc1 = id[u], scc2 = id[v];
if (scc1 != scc2) {
add(head2, scc1, scc2);
deg[scc2]++;
}
}
}
// 将单独的结点连接到0号点上
for (int i = 1; i <= scc_cnt; ++i) {
if (!deg[i]) add(head2, 0, i);
}
dfs(0);
cout << f[0][m] << "\n";
return 0;
}

京公网安备 11010502036488号