【题解】LuoGu2515:[HAOI2010]软件安装

本文介绍了一种结合强连通分量与动态规划(DP)解决特定图论问题的方法。通过缩点和拓扑排序,在有向无环图(DAG)上进行DP求解最大价值问题。代码实现包括了强连通分量的Tarjan算法及DP转移过程。

原题传送门
这题挺简单
首先可以知道一个强连通分量要么全部选,要么全不选
所以缩点
缩好以后建新图,注意连边方向,从 d [ i ] d[i] d[i]连到 i i i
满足拓扑
在DAG上跑DP
d p [ u ] [ m ] 表 示 点 u 时 , 重 量 最 多 为 m 的 最 大 价 值 dp[u][m]表示点u时,重量最多为m的最大价值 dp[u][m]um
转移显然

Code:

#include <bits/stdc++.h>
#define maxn 510
using namespace std;
struct Edge{
    int to, next;
}edge[maxn * maxn];
int num, head[maxn], Index, dfn[maxn], low[maxn], vis[maxn], top, sta[maxn], tot;
int color[maxn], w[maxn], v[maxn], W[maxn], V[maxn], d[maxn], deg[maxn], n, m;
int dp[maxn][maxn];

inline int read(){
    int s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

void addedge(int x, int y){ edge[++num] = (Edge) { y, head[x] }; head[x] = num; }

void tarjan(int u){
    dfn[u] = low[u] = ++Index;
    vis[u] = 1, sta[++top] = u;
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        if (!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]); else
        if (vis[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]){
        ++tot;
        while (sta[top + 1] != u)
            color[sta[top]] = tot, vis[sta[top]] = 0, W[tot] += w[sta[top]], V[tot] += v[sta[top--]];
    }
}

void dfs(int u){
    for (int i = W[u]; i <= m; ++i) dp[u][i] = V[u];
    for (int i = head[u]; i; i = edge[i].next){
        int v = edge[i].to;
        dfs(v);
        for (int j = m; j >= W[u]; --j)
            for (int k = 0; k <= j - W[u]; ++k)
                dp[u][j] = max(dp[u][j], dp[v][k] + dp[u][j - k]);	
    }
}

int main(){
    n = read(), m = read();
    for (int i = 1; i <= n; ++i) w[i] = read();
    for (int i = 1; i <= n; ++i) v[i] = read();
    for (int i = 1; i <= n; ++i) d[i] = read(), addedge(d[i], i);
    for (int i = 1; i <= n; ++i)
        if (!dfn[i]) tarjan(i);
    num = 0;
    memset(head, 0, sizeof(head));
    for (int i = 1; i <= n; ++i)
        if (color[d[i]] != color[i] && d[i]) addedge(color[d[i]], color[i]), ++deg[color[i]];
    for (int i = 1; i <= tot; ++i)
        if (!deg[i]) addedge(tot + 1, i);
    dfs(tot + 1);
    printf("%d\n", dp[tot + 1][m]);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值