题目描述

现在我们的手头有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 )

输出格式:

一个整数,代表最大价值

输入输出样例

输入样例#1

3 10
5 5 6
2 3 4
0 1 1

输出样例#1

5

算法:

树形DP

 

分析:

一看到这道题,就感觉和选课很相似,不过再仔细一看,就发现其实之间大有不同。

选课一题,是不可以成环的,因为先修课的先修课不可能成为后面的后修课,所以,判环是没必要的。

但是这道题,非常坑爹,它可以出现环。若出现a依赖b,b依赖c,c依赖a,那么我们究竟怎么搜索呢,而且转移方程也不好写。

我们通过观察发现,这种环状的问题,要是选了其中一个,那么就意味着要把整个环给选了,要么不选,就得整个都不选。

所以,我们就可以从这里入手,把一个个的环缩成一个个新的点,一个点代表了环的整体。

接下来就可以通过选课的后续思路,建造一棵左儿子右兄弟的二叉树,通过多叉树转二叉树进行记忆化搜索。

 

后面的就很简单了,状态转移方程就是要不要当前的兄弟,还是把机会分给兄弟和孩子。

 

不过中途有很多小点需要注意。

判环要用floyed的方法三重for来判断环,记得三个循环的变量顺序不能错,否则后果会判不到环。

其次,缩点建点的时候,主体分三个情况来判断:

1、这是新环,我需要建点来记录,他的价值和体积我都直接相加就好了。不过我们要记得把已经转移的新点的原来两个旧点序号变成负数,而新点的下标+旧点的内容恰好是等于n的,每一个环一一对应,不多也不会少,详情见代码操作。

2、这是旧环,我发现了新的点,这个点在环内。那么我就把这个点并到环中,把价值和体积也加到新点上,然后把原来这个旧环的密码推到这个新点上。

3、找到一个新点,他和一个旧环有联系,但不属于这个环的一部分,于是把它的从属关系推到新的点上。

 

 

步骤简化为:判环+缩点+多叉转二叉+记忆化搜索。

 

上代码:

 

 #include<cstdio>
#include<iostream>
#include<cctype>
using namespace std; const int size=;
int n,m,ans,sum,d[size],v[size],w[size],f[size][*size],b[size],c[size];
bool a[size][size]; //是否连通 inline int read() //读入优化
{
int f=,x=;
char c=getchar();
while (!isdigit(c))
f=c=='-'?-:,c=getchar();
while (isdigit(c))
x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
} void floyed() //Floyd判环
{
int i,j,k;
for (k=;k<=n;k++)
for (i=;i<=n;i++)
for (j=;j<=n;j++)
if (a[j][k]&&a[k][i])
a[j][i]=;
} void connect() //缩点建点
{
ans=n; //ans表示新点的下标
int i,j;
for (i=;i<=ans;i++)
for (j=;j<=ans;j++)
{
if (a[i][j]&&a[j][i]&&i!=j&&w[i]>&&w[j]>) //发现新环
{
sum--; //sum是旧点的密码
ans++;
w[ans]=w[i]+w[j];
v[ans]=v[i]+v[j];
w[i]=w[j]=sum;
}
else
if (w[d[j]]<&&w[j]>) //发现旧环
{
if (a[d[j]][j]&&a[j][d[j]]) //属于环
{
w[n-w[d[j]]]+=w[j];
v[n-w[d[j]]]+=v[j];
w[j]=w[d[j]];
}
else //不属于环
if (a[d[j]][j]&&!a[j][d[j]]||!a[d[j]][j]&&a[j][d[j]])
d[j]=n-w[d[j]];
}
}
} int dfs(int root,int val) //记忆化搜索
{
if (f[root][val]>)
return f[root][val];
if (root==||val<=)
return ;
f[b[root]][val]=dfs(b[root],val);
f[root][val]=f[b[root]][val];
int i,k=val-w[root];
for (i=;i<=k;i++)
{
f[b[root]][k-i]=dfs(b[root],k-i);
f[c[root]][i]=dfs(c[root],i);
f[root][val]=max(f[root][val],v[root]+f[b[root]][k-i]+f[c[root]][i]);
}
return f[root][val];
} int main()
{
int i;
n=read();
m=read();
for (i=;i<=n;i++)
w[i]=read();
for (i=;i<=n;i++)
v[i]=read();
for (i=;i<=n;i++)
{
d[i]=read();
a[d[i]][i]=;
}
floyed();
connect();
for (i=;i<=ans;i++) //模拟链表
if (w[i]>)
{
b[i]=c[d[i]];
c[d[i]]=i;
}
printf("%d",dfs(c[],m));
return ;
}

这道题其实难度很大,不过只要把原理弄懂,代码还是很好打的,主要就是围绕着如何更加简便地进行记忆化搜索,然后一层层地进行优化,这道题用到的就是判环和缩点的知识。

 

嗯,就这样了。

【洛谷P2515【HAOI2010】】软件安装的更多相关文章

  1. 洛谷 P2515 [HAOI2010]软件安装 解题报告

    P2515 [HAOI2010]软件安装 题目描述 现在我们的手头有\(N\)个软件,对于一个软件\(i\),它要占用\(W_i\)的磁盘空间,它的价值为\(V_i\).我们希望从中选择一些软件安装到 ...

  2. 洛谷—— P2515 [HAOI2010]软件安装

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

  3. 洛谷 P2515 [HAOI2010]软件安装

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

  4. 洛谷——P2515 [HAOI2010]软件安装

    https://www.luogu.org/problem/show?pid=2515#sub 题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中 ...

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

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

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

    传送门 我们可以把每一个$d$看做它的父亲,这样这个东西就构成了一个树形结构 问题是他有可能形成环,所以我们还需要一遍tarjan缩点 缩完点后从0向所有入度为零的点连边 然后再跑一下树形dp就行了 ...

  7. luogu P2515 [HAOI2010]软件安装 |Tarjan+树上背包

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

  8. [bzoj2427]P2515 [HAOI2010]软件安装(树上背包)

    tarjan+树上背包 题目描述 现在我们的手头有 \(N\) 个软件,对于一个软件 \(i\),它要占用 \(W_i\) 的磁盘空间,它的价值为 \(V_i\).我们希望从中选择一些软件安装到一台磁 ...

  9. P2515 [HAOI2010]软件安装

    树形背包 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> ...

  10. luogu P2515 [HAOI2010]软件安装

    传送门 看到唯一的依赖关系,容易想到树型dp,即\(f_{i,j}\)表示选点\(i\)及子树内连通的点,代价为\(j\)的最大价值,然后就是选课那道题 但是要注意 1.题目中的依赖关系不一定是树,可 ...

随机推荐

  1. win8平板APP开发的教程文章

    http://blog.csdn.net/tcjiaan/article/details/7866595 基于C#的Metro工程如何引用C++的动态库——FIleNotFound解决办法: http ...

  2. 【数据库】mysql中复制表结构的方法小结

    mysql中用命令行复制表结构的方法主要有一下几种: 1.只复制表结构到新表 ? 1 CREATE TABLE 新表 SELECT * FROM 旧表 WHERE 1=2 或者 ? 1 CREATE ...

  3. 【Django】Django—Form两种解决表单数据无法动态刷新的方法

    一.无法动态更新数据的实例 1. 如下,数据库中创建了班级表和教师表,两张表的对应关系为“多对多” from django.db import models class Classes(models. ...

  4. 【bzoj4027】[HEOI2015]兔子与樱花 树形dp+贪心

    题目描述 很久很久之前,森林里住着一群兔子.有一天,兔子们突然决定要去看樱花.兔子们所在森林里的樱花树很特殊.樱花树由n个树枝分叉点组成,编号从0到n-1,这n个分叉点由n-1个树枝连接,我们可以把它 ...

  5. BZOJ4890 Tjoi2017城市

    显然删掉的边肯定是直径上的边.考虑枚举删哪一条.然后考虑怎么连.显然新边应该满足其两端点在各自树中作为根能使树深度最小.只要线性求出这个东西就可以了,这与求树的重心的过程类似. #include< ...

  6. 题解 P2955 【[USACO09OCT]奇数偶数Even? Odd? 】

    很明显这题是个假入门! 小金羊一不小心点进题解发现了内幕 能看的出来都WA过Unsigned long long int 做题可以用Python,Python的变量虽然 强悍的不行! 但是我们可以用字 ...

  7. 【刷题】BZOJ 2434 [Noi2011]阿狸的打字机

    Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的 ...

  8. NOIP2017 宝藏 题解报告【状压dp】

    题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但是 ...

  9. (转)搭建本地 8.8 W 乌云漏洞库

    下载地址: 开源地址: https://github.com/m0l1ce/wooyunallbugs 百度网盘: 链接: http://pan.baidu.com/s/1nvkFKox 密码: 94 ...

  10. 浴谷八连测R6题解(收获颇丰.jpg)

    这场的题都让我收获颇丰啊QWQ 感谢van♂老师 T1 喵喵喵!当时以为经典题只能那么做, 思维定势了... 因为DP本质是通过一些条件和答案互相递推的一个过程, 实际上就是把条件和答案分配在DP的状 ...