题解【bzoj2427 [HAOI2010]软件安装】
Description
现在我们的手头有\(N\)个软件,对于一个软件\(i\),它要占用\(W_i\)的磁盘空间,它的价值为\(V_i\)。我们希望从中选择一些软件安装到一台磁盘容量为\(M\)计算机上,使得这些软件的价值尽可能大(即\(V_i\)的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件\(i\)只有在安装了软件\(j\)(包括软件j的直接或间接依赖)的情况下才能正确工作(软件\(i\)依赖软件\(j\))。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为\(0\)。
我们现在知道了软件之间的依赖关系:软件\(i\)依赖软件\(D_i\)。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则\(D_i=0\),这时只要这个软件安装了,它就能正常工作。
Solution
明显是树形dp。设\(dp[i][j]\)为以\(i\)号点为根的子树中用不超过\(j\)的空间的最大价值。
但是这道题所给出的条件不能直接构成一棵树,比如\(d[1]=2,d[2]=3,d[3]=1\)这时\(1,2,3\)便形成一个独立的联通块并且构成环。又由于这个环也很特殊:要么都选,要么都不选,所以可以用tarjan将环缩点。新点的\(w=\sum\limits_{e \in \text{该环}}w[e]\),\(v=\sum\limits_{e \in \text{该环}}v[e]\)。
缩点后将原来的联通块之间的边连好后再从\(0\)向每一个加完边后入度为\(0\)的点连一条边,此时就将原图转换为一颗以\(0\)为根的树,然后就可以愉快的树形dp辣。
举个栗子:
如果最开始图是这样的

然后缩点,将\(1,2,3\)缩为\(14\),\(10,11,12,13\)缩为\(15\),然后让\(0\)向几个联通块连边:

这时原图被转换成对答案等价的一棵树,然后\(dfs\)用上述方程进行简单的树上背包就解决了。
code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 505;
int n, m, cnt, w[MAXN], a[MAXN], d[MAXN];
int dfn[MAXN], low[MAXN], bel[MAXN], tot, scc, ins[MAXN], sta[MAXN], top;
int W[MAXN], V[MAXN], indeg[MAXN], dp[MAXN][MAXN];
struct edge {
int v;
edge *next;
}pool[MAXN * 2], *head[MAXN];
inline void addedge(int u, int v) {
edge *p = &pool[++cnt];
p->v = v, p->next = head[u], head[u] = p;
}
void tarjan(int u) {
dfn[u] = low[u] = ++tot; sta[++top] = u; ins[u] = 1;
for(edge *p = head[u]; p; p = p->next) {
int v = p->v;
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(ins[v])
low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
++scc;
while(sta[top + 1] != u) {
bel[sta[top]] = scc;
W[scc] += w[sta[top]];
V[scc] += a[sta[top]];
ins[sta[top--]] = 0;
}
}
}
void solve(int u) {
for(int i = W[u]; i <= m; i++)
dp[u][i] = V[u];
for(edge *p = head[u]; p; p = p->next) {
int v = p->v;
solve(v); int k = m - W[u];
for(int i = k; i >= 0; i--)
for(int j = 0; j <= i; j++)
dp[u][i + W[u]] =
max(dp[u][i + W[u]],
dp[v][j] + dp[u][i + W[u] - j]);
}
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) {
scanf("%d", &d[i]); if(d[i]) addedge(d[i], i);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
for(int i = 0; i <= n; i++) head[i] = NULL; cnt = 0;
for(int i = 1; i <= n; i++)
if(bel[d[i]] != bel[i]) {
addedge(bel[d[i]], bel[i]);
indeg[bel[i]]++;
}
for(int i = 1; i <= scc; i++)
if(!indeg[i]) addedge(0, i);
solve(0);
printf("%d\n", dp[0][m]);
return 0;
}
题解【bzoj2427 [HAOI2010]软件安装】的更多相关文章
- [BZOJ2427][HAOI2010]软件安装(Tarjan+DP)
2427: [HAOI2010]软件安装 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1987 Solved: 791[Submit][Statu ...
- bzoj2427:[HAOI2010]软件安装(Tarjan+tree_dp)
2427: [HAOI2010]软件安装 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1053 Solved: 424[Submit][Statu ...
- bzoj2427: [HAOI2010]软件安装
Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...
- [bzoj2427][HAOI2010]软件安装——强连通分量+树形DP
题目大意 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是 ...
- [BZOJ2427]:[HAOI2010]软件安装(塔尖+DP)
题目传送门 题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用${W}_{i}$的磁盘空间,它的价值为${V}_{i}$.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件 ...
- BZOJ2427:[HAOI2010]软件安装(树形DP,强连通分量)
Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...
- [BZOJ2427][HAOI2010]软件安装-tarjan缩点-树上dp
<题面> 这个题真伤人 之前Tarjan和树规都没学好,吃了不少亏,仔仔细细的搞了一天,收获颇丰 先来一个Tarjan的链接:$\mathbb{O}$ 题目的数据比较友好: $dp$不对: ...
- BZOJ2427: [HAOI2010]软件安装 tarjan+树形背包
分析: 一开始我以为是裸的树形背包...之后被告知这东西...可能有环...什么!有环! 有环就搞掉就就可以了...tarjan缩点...建图记得建立从i到d[i]之后跑tarjan,因为这样才能判断 ...
- [BZOJ2427][HAOI2010]软件安装(tarjan+树形DP)
如果依赖关系出现环,那么对于一个环里的点,要么都选要么都不选, 所以每个环可以当成一个点,也就是强连通分量 然后就可以构造出一颗树,然后树形背包瞎搞一下就行了 注意要搞一个虚拟节点当根节点 Code ...
随机推荐
- 2018牛客多校第二场a题
一个人可以走一步或者跳x步,但不能连着跳,问到这个区间里有几种走法 考虑两种状态 对于这一点,我可以走过来,前面是怎么样的我不用管,也可以跳过来但是,跳过来必须保证前一步是走的 dp[i][0]表示 ...
- RL_Learning
Key Concepts in RL 标签(空格分隔): RL_learning OpenAI Spinning Up原址 states and observations (状态和观测) action ...
- 共识算法 pos,Dpos
在之前讲解了比特币中的共识算法pow(proot of work),我们先来简单的回顾一下. 新的交易将会广播给所有节点. 每个节点将都会讲新的交易收集到一个区块中. 每个节点都在为其区块收集困难的工 ...
- Bitcoin: A Peer-to-Peer Electronic Cash System
Bitcoin: A Peer-to-Peer Electronic Cash System Satoshi Nakamoto October 31, 2008 Abstract A purely p ...
- 《剑指Offer》题二十一~题三十
二十一.调整数组顺序使奇数位于偶数前面 题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分. 测试用例: 功能测试:输入数组中的奇 ...
- Android蓝牙开发浅谈(转)
http://www.eoeandroid.com/thread-18993-1-1.html 对于一般的软件开发人员来说,蓝牙是很少用到的,尤其是Android的蓝牙开发,国内的例子很少 A ...
- 《剑指offer》---跳台阶问题
本文算法使用python3实现 1. 问题1 1.1 题目描述: 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 时间限制:1s:空间限制:3276 ...
- thrift多平台安装
thrift支持多语言的RPC,一直都想深入学习了解thrift,最近有空,就上网查了些资料,学习了一下,对它的使用有了一些了解.本篇是写thrift的安装,使用方法会另起一篇来写. 本文使用thri ...
- MVC中验证码的实现(经常用,记录备用)
一.目录 1.多层架构+MVC+EF+AUTOFAC+AUTOMAPPER: 2.MVC中验证码的实现(经常用,记录备用) 3.Ligerui首页的快速搭建 二 正文 Ok,我们的验证码开始,这篇文章 ...
- 【Docker 命令】- push 命令
docker push : 将本地的镜像上传到镜像仓库,要先登陆到镜像仓库 语法 docker push [OPTIONS] NAME[:TAG] OPTIONS说明: --disable-conte ...