题目大意

现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

题解

根据题目,我们建立图。

显然这个图由一些树和一些scc构成(注意:scc一定不在树上),那么我们可以知道,如果选了scc中的一个点,其他点必须也要选,所以我们把所有的scc缩成一个点,这样就构成了一个森林。

对于一个入度为0的点,我们从一个虚点向其连接一条边,这样图就变成了树。

考虑树形dp,定义f[i][j]为对于i为根的子树总共分配j点权值能拿到的最大value

我们可以有$$f[i][j] = f[k][l] + f[i][j-l]$$

记忆化搜索即可。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = 505;
const ll maxm = 1000;
ll n, m, K, s, ans = 0;
ll w[maxn], v[maxn], W[maxn], V[maxn];
ll cnt[maxn], vis[maxn], in[maxn], f[maxn][maxm];
vector<ll> sc[maxn];
vector<ll> vs;
vector<ll> G[maxn];
vector<ll> rg[maxn];
vector<ll> ng[maxn];
void add(ll from, ll to) {
  G[from].push_back(to);
  rg[to].push_back(from);
}
void add_edge(ll from, ll to) {
  in[to] = 1;
  ng[from].push_back(to);
}
void dfs(ll s) {
  vis[s] = 1;
  for (ll i = 0; i < G[s].size(); i++) {
    if (!vis[G[s][i]])
      dfs(G[s][i]);
  }
  vs.push_back(s);
}
void rdfs(ll s, ll k) {
  vis[s] = 1;
  for (ll i = 0; i < rg[s].size(); i++) {
    if (!vis[rg[s][i]])
      rdfs(rg[s][i], k);
  }
  cnt[s] = k;
  sc[k].push_back(s);
}
void scc() {
  memset(vis, 0, sizeof(vis));
  vs.clear();
  for (ll i = 1; i <= n; i++) {
    if (!vis[i])
      dfs(i);
  }
  ll k = 0;
  memset(vis, 0, sizeof(vis));
  for (ll i = vs.size() - 1; i >= 0; i--) {
    if (!vis[vs[i]])
      rdfs(vs[i], k++);
  }
  K = k;
}
void build_graph() {
  for (ll i = 0; i < K; i++) {
    for (ll j = 0; j < sc[i].size(); j++) {
      W[i] += w[sc[i][j]];
      V[i] += v[sc[i][j]];
    }
  }
  for (ll i = 1; i <= n; i++) {
    for (ll j = 0; j < G[i].size(); j++) {
      if (cnt[i] != cnt[G[i][j]])
        add_edge(cnt[i], cnt[G[i][j]]);
    }
  }
  s = K + 1;
  for (ll i = 0; i < K; i++)
    if (!in[i])
      add_edge(s, i);
}
void dp(ll x) {
  for (ll i = 0; i < ng[x].size(); i++) {
    dp(ng[x][i]);
    for (ll j = m - W[x]; j >= 0; j--) { //鏋氫妇閫夊畬鑷繁鍚庤垂鐢?
      for (ll k = 0; k <= j; k++) {      //鏋氫妇缁欏効瀛愮殑璐圭敤
        f[x][j] = max(f[x][j], f[x][k] + f[ng[x][i]][j - k]);
      }
    }
  }
  for (ll j = m; j >= 0; j--) {
    if (j >= W[x])
      f[x][j] = f[x][j - W[x]] + V[x];
    else
      f[x][j] = 0;
  }
}
int main() {
  // freopen("input", "r", stdin);
  scanf("%lld %lld", &n, &m);
  for (ll i = 1; i <= n; i++)
    scanf("%lld", &w[i]);
  for (ll i = 1; i <= n; i++)
    scanf("%lld", &v[i]);
  for (ll i = 1; i <= n; i++) {
    ll x;
    scanf("%lld", &x);
    if (x)
      add(x, i);
  }
  scc();
  build_graph();
  dp(s);
  printf("%lld\n", f[s][m]);
}

[bzoj2427][HAOI2010]软件安装——强连通分量+树形DP的更多相关文章

  1. bzoj2427 [HAOI2010]软件安装——缩点+树形DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2427 今天的考试题...好不容易一次写对了树形DP,却没发现有环的情况... 发现自己 ta ...

  2. BZOJ 2427 软件安装(强连通分量+树形背包)

    题意:现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大).但是现在有 ...

  3. 洛谷 P2515 [HAOI2010]软件安装(缩点+树形dp)

    题面 luogu 题解 缩点+树形dp 依赖关系可以看作有向边 因为有环,先缩点 缩点后,有可能图不联通. 我们可以新建一个结点连接每个联通块. 然后就是树形dp了 Code #include< ...

  4. bzoj 2427: [HAOI2010]软件安装【tarjan+树形dp】

    一眼最大权闭合子图,然后开始构图,画了画之后发现我其实是个智障网络流满足不了m,于是发现正确的打开方式应该是一眼树上dp 然后仔细看了看性质,发现把依赖关系建成图之后是个奇环森林,这个显然不能直接dp ...

  5. [BZOJ2427][HAOI2010]软件安装(Tarjan+DP)

    2427: [HAOI2010]软件安装 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1987  Solved: 791[Submit][Statu ...

  6. bzoj2427:[HAOI2010]软件安装(Tarjan+tree_dp)

    2427: [HAOI2010]软件安装 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1053  Solved: 424[Submit][Statu ...

  7. BZOJ2427:[HAOI2010]软件安装(树形DP,强连通分量)

    Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...

  8. [BZOJ2427][HAOI2010]软件安装(tarjan+树形DP)

    如果依赖关系出现环,那么对于一个环里的点,要么都选要么都不选, 所以每个环可以当成一个点,也就是强连通分量 然后就可以构造出一颗树,然后树形背包瞎搞一下就行了 注意要搞一个虚拟节点当根节点 Code ...

  9. [BZOJ2427]:[HAOI2010]软件安装(塔尖+DP)

    题目传送门 题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用${W}_{i}$的磁盘空间,它的价值为${V}_{i}$.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件 ...

随机推荐

  1. css的水平居中和垂直居中总结

    Html代码: <div class="md-warp"> <div class="md-main">块级元素</div> ...

  2. ios下 active 演示激活

    document.body.addEventListener('touchstart', function () { });

  3. Qt 实现脉搏检测-2,简陋的功能产品

    今天终于可以接上硬件来显示真是的脉搏情况了,上图 主要就是显示脉搏的心跳曲线,和IBI 数据来源是三个,串口,网口和蓝牙,目前只实现了串口,过程应该都是差不多的,监听,读取,解析,等硬件更新后,再次更 ...

  4. Laxcus大数据管理系统2.0 (1) - 摘要和目录

    Laxcus大数据管理系统 (version 2.0) Laxcus大数据实验室 摘要 Laxcus是Laxcus大数据实验室全体系全功能设计研发的多用户多集群大数据管理系统,支持一到百万台级节点,提 ...

  5. 阿里云SLB上http强制跳转到https问题处理

    背景: 最近一客户有一个需求,需要将外网所有http访问请求强制跳转到https,公网出口使用阿里云SLB,证书放在SLB上,SLB后端实例为ECS(webserver)web服务使用nginx, 网 ...

  6. SPOJ 694

    题面 题意: 给一个字符串,求它有多少个不同的子串 多组数据. Solution : 模板题,用所有的减去重复的即可. #include <cstdio> #include <alg ...

  7. 洛谷P1189'SEARCH'

    题目描述 年轻的拉尔夫开玩笑地从一个小镇上偷走了一辆车,但他没想到的是那辆车属于警察局,并且车上装有用于发射车子移动路线的装置. 那个装置太旧了,以至于只能发射关于那辆车的移动路线的方向信息. 编写程 ...

  8. C++ 学习笔记之——文件操作和文件流

    1. 文件的概念 对于用户来说,常用到的文件有两大类:程序文件和数据文件.而根据文件中数据的组织方式,则可以将文件分为 ASCII 文件和二进制文件. ASCII 文件,又称字符文件或者文本文件,它的 ...

  9. K-Means和FCM聚类

    K均值聚类是基于原型的.划分的聚类方法.聚类数K由用户指定,初始的K个聚类中心随机选取,然后将每个点分派到最近的聚类中心,形成K个簇,接下来重新计算每个簇的聚类中心,重复上一步,直到簇不发生变化或达到 ...

  10. c# 对List<T> 某字段排序,取TOP条数据

    //排序的对象里的字段数据准备 try { cmr.v4 = Double.Parse(cmr.v3) - Double.Parse(cmr.v2); } catch (Exception e) { ...