题目描述

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

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

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

输入

第1行:N, M  (0<=N<=100, 0<=M<=500)
      第2行:W1, W2, ... Wi, ..., Wn (0<=Wi<=M )
      第3行:V1, V2, ..., Vi, ..., Vn  (0<=Vi<=1000 )
      第4行:D1, D2, ..., Di, ..., Dn(0<=Di<=N, Di≠i )

输出

一个整数,代表最大价值。

样例输入

3 10
5 5 6
2 3 4
0 1 1

样例输出

5

来源

Day2

(转自http://www.lydsy.com/JudgeOnline/problem.php?id=2427


  这道题看起来像是树归,首先来判断一下,一个软件只能依赖于一个软件,正如树,一个节点只有一个父节点(除了根节点)(把所有节点都连接到0号节点下),但是来构思一组数据:

  D:

  像这样,1依赖于2,2依赖于3.,3依赖于1,构成了一个环(强连通分量),明显不符合树的性质,但是仔细想想,要么全选要么全不选

就可以把它当成一个节点来看待。

  至于缩点。。。这个也比较简单,和codevs上"爱在心中"差不多,首先给belong数组赋初值:

for(int i = ;i <= n;i++)
belong[i] = i;

  再Tarjan一次,将元素弹出栈时要将对应belong数组中这个强连通分量的值全部设成这中间任意元素的值(但必须一样)

接着for循环扫描一次,凡是belong[i] != i的像这样处理一下:

w[belong[i]] += w[i];
v[belong[i]] += v[i];

  如果出现访问d[i]就像这样访问:

d[belong[i]]......

  是不是十分方便快捷?(除了Tarjan算法的代码复杂度)

  下面思考一下树归方程,感觉如果玩多叉树的话状态很多,就转成二叉树,为了代码简洁,有这么两种方法

第一种方法是记录每个节点添加进的"兄弟"的地址,就加一个指针变量,添加一个"儿子"或者"兄弟"时,就访问

这个对应的指针变量,先正常加入,然后把指针指向它

for(int i = ;i <= n;i++){
for(int j = head[i];j;j = edge[j].next){
if(belong[i] == i && belong[i] != belong[edge[j].end]){
if(node[i]->left != NULL)
node[i]->left->bro = &node[edge[belong[j]].end];
else node[i]->left = &node[edge[belong[j]].end];;
}
}
}

  是不是显得有点麻烦?而且严重牺牲了可读性,在看了某大神的代码后,我知道了这种方法:

用bro[i]储存i节点的右子树(原先的"兄弟"),用son[i]储存i节点的左子树(原先的"子节点"),

然后:

最后:

代码实现也比较简单:

for(int i = ;i <= n;i++){
for(int j = head[i];j;j = edge[j].next){
if(belong[i] == i && belong[i] != belong[edge[j].end]){
bro[i] = son[belong[edge[j].end]];
son[belong[edge[j].end]] = i;
}
}
}

  另外关于左右子树出现空的时候,如果多加几个if语句的话,可能树形DP的代码就和Tarjan算法有的一拼了,可以把

它的左右子树的位置设成一个值为0的节点,这就相当于一个空节点,但并不影响计算结果

  写出树归方程:

f[index][i] = max(f[index][i],v[index] + f[left][j] + f[right][limit - j]);

(index是第index个节点,limit是i - w[i],left = son[index],right = bro[index])

最后附上我超级不简洁的代码(如果想要速度更快,可以把cin,cout改成scanf和printf)

Code:

 /**
* bzoj
* Problem#2427
* Accepted
* Time:220ms
* Memory:1492k
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<vector>
#define _min(a,b) ((a)<(b))?(a):(b)
using namespace std;
typedef bool boolean;
typedef class Edge{
public:
int end;
int next;
Edge():end(),next(){}
Edge(int end,int next):end(end),next(next){}
}Edge;
Edge *edge;
int n,m;
int *w; //软件大小
int *v; //价值
int *d;
int *head;
int top;
inline void addEdge(int from,int end){
top++;
edge[top].next=head[from];
edge[top].end=end;
head[from]=top;
}
boolean *visited;
int *visitID;
int *exitID;
int entryed;
stack<int> sta;
int *belong;
boolean *inStack;
int *bro;
int *son;
void getSonMap(int end){
int now=-;
int exits=;
while(now!=end){
now=sta.top();
belong[now]=end;
inStack[now]=false;
exits++;
sta.pop();
}
}
void Tarjan(const int pi){
int index=head[pi];
visitID[pi]=++entryed;
exitID[pi]=visitID[pi];
visited[pi]=true;
inStack[pi]=true;
sta.push(pi);
while(index!=){
if(!visited[edge[index].end]){
Tarjan(edge[index].end);
exitID[pi]=_min(exitID[pi],exitID[edge[index].end]);
}else if(inStack[edge[index].end]){
exitID[pi]=_min(exitID[pi],visitID[edge[index].end]);
}
index=edge[index].next;
}
if(exitID[pi]==visitID[pi]){
getSonMap(pi);
}
}
void rebuild(){
vector<int> indexs;
for(int i=;i<=n;i++){
if(belong[i] != i){
v[belong[i]] += v[i];
w[belong[i]] += w[i];
indexs.push_back(belong[i]);
}
}
for(int i = ;i < indexs.size();i++)
edge[head[indexs[i]]].end = ;
}
void create(){
bro = new int[(const int)(n + )];
son = new int[(const int)(n + )];
for(int i = ;i <= n;i++){
son[i] = n + ;
bro[i] = n + ;
}
for(int i = ;i <= n;i++){
for(int j = head[i];j;j = edge[j].next){
if(belong[i] == i && belong[i] != belong[edge[j].end]){
bro[i] = son[belong[edge[j].end]];
son[belong[edge[j].end]] = i;
}
}
}
}
int f[][];
void solve(int index){
if(index > n) return ;
solve(son[index]);
solve(bro[index]);
int left = son[index];
int right = bro[index];
for(int i = ;i <= m;i++){
f[index][i] = max(f[index][i],f[right][i]);
int limit = i - w[index];
for(int j = ;j <= limit;j++){
f[index][i] = max(f[index][i],v[index] + f[left][j] + f[right][limit - j]);
}
}
}
int main(){
cin>>n>>m;
head=new int[(const int)(n+)];
edge=new Edge[(const int)(n+)];
visited=new boolean[(const int)(n+)];
visitID=new int[(const int)(n+)];
exitID =new int[(const int)(n+)];
belong =new int[(const int)(n+)];
inStack=new boolean[(const int)(n+)];
memset(head,,sizeof(int)*(n+));
memset(visited,false,sizeof(boolean)*(n+));
memset(inStack,false,sizeof(boolean)*(n+));
w = new int[(const int)(n + )];
v = new int[(const int)(n + )];
d = new int[(const int)(n + )];
for(int i=;i<=n;i++)
scanf("%d",&w[i]);
for(int i = ;i <= n;i++)
scanf("%d",&v[i]);
for(int i = ;i <= n;i++){
scanf("%d",&d[i]);
addEdge(i, d[i]);
}
for(int i=;i<=n;i++) belong[i]=i;
for(int i=;i<=n;i++){
if(!visited[i])
Tarjan(i);
}
delete[] visited;
delete[] inStack;
rebuild();
delete[] exitID;
delete[] visitID;
create();
w[] = ;
v[] = ;
solve();
printf("%d",f[][m]);
return ;
}

bzoj 2427 软件安装 - Tarjan - 树形动态规划的更多相关文章

  1. BZOJ 2427: [HAOI2010]软件安装 tarjan + 树形背包

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

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

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

  3. 【BZOJ-2427】软件安装 Tarjan + 树形01背包

    2427: [HAOI2010]软件安装 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 960  Solved: 380[Submit][Status ...

  4. [BZOJ 2427] 软件安装

    Link: BZOJ 2427 传送门 Solution: 只看样例的话会以为是裸的树形$dp$…… 但实际上题目并没有说明恰好仅有一个物品没有依赖项 因此原图可能由是由多棵树与多个图组成的 先跑一遍 ...

  5. 【bzoj2427】[HAOI2010]软件安装 Tarjan+树形背包dp

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

  6. 【BZOJ2427】[HAOI2010]软件安装 Tarjan+树形背包

    [BZOJ2427][HAOI2010]软件安装 Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为 ...

  7. BZOJ2427: [HAOI2010]软件安装 tarjan+树形背包

    分析: 一开始我以为是裸的树形背包...之后被告知这东西...可能有环...什么!有环! 有环就搞掉就就可以了...tarjan缩点...建图记得建立从i到d[i]之后跑tarjan,因为这样才能判断 ...

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

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

  9. BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP

    BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP 题意: 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁 ...

随机推荐

  1. Myers–Briggs_Type_Indicator 迈尔斯布里格斯类型指标(MBTI)

    Myers–Briggs Type Indicator - Wikipedia https://en.wikipedia.org/wiki/Myers%E2%80%93Briggs_Type_Indi ...

  2. Indexes (also called “keys” in MySQL)

    High Performance MySQL, Third Edition by Baron Schwartz, Peter Zaitsev, and Vadim Tkachenko   Is an ...

  3. Can you answer these queries?---hdu4027

    题目链接 有n个数:当操作为1时求L到R的和: 当操作为0时更新L到R为原来的平方根: 不过如果仔细演算的话会发现一个2^64数的平方根开8次也就变成了 1,所以也更新不了多少次,所以可以每次更新到底 ...

  4. Python开发【模块】:Requests(二)

    Requests模块常见的4中post请求 HTTP 协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式.常见的四种编码方式如下: 1 ...

  5. sqlserver 索引的结构及其存储,索引内容

    sqlserver 索引的结构及其存储,sql server索引内容 文章转载,原文地址: http://www.cnblogs.com/panchunting/p/SQLServer_IndexSt ...

  6. python len() 函数

    Python len() Python len() 方法返回对象(字符.列表.元组等)长度或项目个数. len(obj) 方法语法 obj -- 对象(字符串.列表.元组.字典等) 字符串长度 > ...

  7. 超级详细的RedGateSqlServer工具教程,效率提高的不止一点点之SQLPrompt

    距离上次SQLDoc教程贴过去2个月了,时间真快,好了,废话不多说,继续 http://pan.baidu.com/share/link?shareid=177401896&uk=330822 ...

  8. 空类指针为什么可以调用类的成员函数 以及 A(){}和A();

    1. 代码及问题 #include <iostream> using namespace std; class A { public: A() {} //A *p = new A()时:此 ...

  9. 阻止提交按钮的默认 action

    使用 preventDefault() 函数来阻止对表单的提交. 示例代码如下: <html><head><script type="text/javascri ...

  10. 【SVM】A Practical Guide to Support Vector Classi cation

    零.简介 一般认为,SVM比神经网络要简单. 优化目标: