题目描述

现在我们的手头有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. erlang调优方法

    1. 来自Scaling Erlang的方法 内核调优: # Increase the ipv4 port range: sysctl -w net.ipv4.ip_local_port_range= ...

  2. bash 基础

    bash是一些基本的命令组成,要想学好bash脚本,基本命令是必须要记住 红色标记为着重点 1执行命令,利用状态返回值来判断 0 : 成功 1-255:失败 2: 测试表达式 [  expressio ...

  3. flink写入mysql的两种方式

    方式一 通过JDBCOutputFormat 在flink中没有现成的用来写入MySQL的sink,但是flink提供了一个类,JDBCOutputFormat,通过这个类,如果你提供了jdbc的dr ...

  4. js get selected text

    js get selected text https://stackoverflow.com/questions/3170648/how-to-get-javascript-select-boxs-s ...

  5. Android内存泄漏第二课--------(集合中对象没清理造成的内存泄漏 )

    一.我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大.如果这个集合是static的话,那情况就更严重 ...

  6. 第207天:HTTP协议头字段详解大全

    本篇重点介绍一下HTTP常用的Header HTTP Header非常之多,很少有人能完全分清这些Header到底是干什么的.鉴于RFC文件规范艰深晦涩难懂,本文对协议规范中列出的HTTP Heade ...

  7. 【BZOJ3504】危桥(网络流)

    [BZOJ3504]危桥(网络流) 题面 BZOJ 洛谷 Description Alice和Bob居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1.某些岛屿之间有桥相连,桥上的道路是双 向的, ...

  8. 洛谷 P2195 HXY造公园 解题报告

    P2195 HXY造公园 题目描述 现在有一个现成的公园,有\(n\)个休息点和\(m\)条双向边连接两个休息点.众所周知,\(HXY\)是一个\(SXBK\)的强迫症患者,所以她打算施展魔法来改造公 ...

  9. poj 1655 树的重心

    Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 13178   Accepted: 5565 De ...

  10. Centos7中使用ipset

      1.禁用firewalld systemctl stop firewalld systemctl disable firewalld   2.安装ipset yum -y install ipse ...