HAOI2010软件安装(树形背包)
HAOI2010软件安装(树形背包)
题意
有n个物品,每个物品最多会依赖一个物品,但一个物品可以依赖于一个不独立(依赖于其它物品)的物品,且可能有多个物品依赖一个物品,并且依赖关系可能形成一个环。现给你V的资金,问如何分配资金,可以使你的得到的总价值最大,请求出这个总价值。
解法
我以前写过对于普通依赖性背包的博客:noip2006金明的预算方案如果对依赖性背包不是很熟悉的同学可以先看一下这道题。
由于这道题的依赖关系可能形成环,所以我们先用tarjan缩一下点,然后依赖关系图就变成了一个森林,这时候我们再将每一棵树的根节点向0号结点连一条边,表示他们依赖0号结点。这时候我们就得到了一颗依赖关系树。
那么现在的重点就在如何处理这颗依赖关系树。因为树上每一个节点的决策数都太大了,所以同样的我们考虑求出每一个以i节点为根的子树在任意权值下的最大价值,然后再在i节点利用01背包来合并,一直递归往上。最后的答案就是0号结点所有方案中最优的那一个。
ps:如果还是看不懂的话,可以参观这里和这里我也是在那儿学的。
代码
以下的代码是 \(O(nv^2)\) 的:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <vector>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
x=0;T k=1;char c=getchar();
while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
}
const int maxn=2500+15;
int w[maxn],cost[maxn];
int n,m,ecnt;
struct Edge{
int u,v;
Edge(int u,int v):u(u),v(v){}
};
vector<Edge> edge;
vector<int> G[maxn];
void add_edge(int u,int v) {
edge.push_back(Edge(u,v));
ecnt=edge.size();
G[u].push_back(ecnt-1);
}
int dfn[maxn],low[maxn],sta[maxn],top,bl[maxn],cnt;
bool ins[maxn];
void tarjian(int u) {
ins[u]=1;sta[++top]=u;
dfn[u]=low[u]=++cnt;
for(int i=0;i<G[u].size();i++) {
Edge e=edge[G[u][i]];
int v=e.v;
if(!dfn[v]) {
tarjian(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]) {
int y;
while((y=sta[top--])&&y) {
bl[y]=u;
ins[y]=0;
if(u==y) break;
w[u]+=w[y],cost[u]+=cost[y];
}
}
}
int dp[maxn][maxn];
void dfs(int u) {
dp[u][cost[u]]=w[u];
for(int i=0;i<G[u].size();i++) {
Edge e=edge[G[u][i]];
int v=e.v;
dfs(v);
for(int i=m;i>=cost[u];i--)
for(int j=0;j<=i-cost[u];j++)
dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]);
}
}
bool noroot[maxn];
int main()
{
read(n),read(m);
for(int i=1;i<=n;i++) read(cost[i]);
for(int i=1;i<=n;i++) read(w[i]);
for(int i=1;i<=n;i++) {
int d;read(d);
if(!d) continue;
add_edge(d,i);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i);
int k=edge.size();
for(int i=0;i<=n;i++) G[i].clear();
for(int i=0;i<k;i++) {
Edge e=edge[i];
int f1=bl[e.u],f2=bl[e.v];
if(f1==f2) continue;// 新的两个点可能在同一个强连通分量中!!
add_edge(f1,f2);noroot[f2]=1;
}
for(int i=1;i<=n;i++) if((bl[i]==i)&&(!noroot[i])) add_edge(0,i);
dfs(0);
int ans=0;
for(int i=0;i<=m;i++)
ans=max(ans,dp[0][i]);
printf("%d\n",ans);
return 0;
}
这下面的是参照徐持横的算法做到的 \(O(nv)\) 的算法:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>
#include <vector>
#define INF 2139062143
#define MAX 0x7ffffffffffffff
#define del(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
template<typename T>
inline void read(T&x)
{
x=0;T k=1;char c=getchar();
while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}x*=k;
}
const int maxn=500+15;
int w[maxn],cost[maxn];
int n,m,ecnt;
struct Edge{
int u,v;
Edge(int u,int v):u(u),v(v){}
};
vector<Edge> edge;
vector<int> G[maxn];
void add_edge(int u,int v) {
edge.push_back(Edge(u,v));
ecnt=edge.size();
G[u].push_back(ecnt-1);
}
int dfn[maxn],low[maxn],sta[maxn],top,bl[maxn],cnt;
bool ins[maxn];
void tarjian(int u) {
ins[u]=1;sta[++top]=u;
dfn[u]=low[u]=++cnt;
for(int i=0;i<G[u].size();i++) {
Edge e=edge[G[u][i]];
int v=e.v;
if(!dfn[v]) {
tarjian(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]) {
int y;
while((y=sta[top--])&&y) {
bl[y]=u;
ins[y]=0;
if(u==y) break;
w[u]+=w[y],cost[u]+=cost[y];
}
}
}
int dp[maxn][maxn];
void dfs(int u,int M) {
if(M<=0) return;
for(int i=0;i<G[u].size();i++) {
Edge e=edge[G[u][i]];
int v=e.v;
for(int j=1;j<=M;j++) dp[v][j]=dp[u][j];
dfs(v,M-cost[v]);
for(int j=cost[v];j<=M;j++) dp[u][j]=max(dp[u][j],dp[v][j-cost[v]]+w[v]);
}
}
bool noroot[maxn];
int main()
{
read(n),read(m);
for(int i=1;i<=n;i++) read(cost[i]);
for(int i=1;i<=n;i++) read(w[i]);
for(int i=1;i<=n;i++) {
int d;read(d);
if(!d) continue;
add_edge(d,i);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjian(i);
int k=edge.size();
for(int i=0;i<=n;i++) G[i].clear();
for(int i=0;i<k;i++) {
Edge e=edge[i];
int f1=bl[e.u],f2=bl[e.v];
if(f1==f2) continue;// 新的两个点可能在同一个强连通分量中!!
add_edge(f1,f2);noroot[f2]=1;
}
for(int i=1;i<=n;i++) if((bl[i]==i)&&(!noroot[i])) add_edge(0,i);
dfs(0,m);
int ans=0;
for(int i=0;i<=m;i++) ans=max(ans,dp[0][i]);
printf("%d",ans);
return 0;
}
HAOI2010软件安装(树形背包)的更多相关文章
- BZOJ2427:[HAOI2010]软件安装(树形DP,强连通分量)
Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和 ...
- 【BZOJ2427】[HAOI2010]软件安装 Tarjan+树形背包
[BZOJ2427][HAOI2010]软件安装 Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为 ...
- BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP
BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP 题意: 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁 ...
- Tarjan+树形DP【洛谷P2515】[HAOI2010]软件安装
[洛谷P2515][HAOI2010]软件安装 题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得 ...
- [HAOI2010]软件安装(Tarjan,树形dp)
[HAOI2010]软件安装 题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可 ...
- bzoj 2427 [HAOI2010]软件安装 Tarjan缩点+树形dp
[HAOI2010]软件安装 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2029 Solved: 811[Submit][Status][Dis ...
- 洛谷 P2515 [HAOI2010]软件安装 解题报告
P2515 [HAOI2010]软件安装 题目描述 现在我们的手头有\(N\)个软件,对于一个软件\(i\),它要占用\(W_i\)的磁盘空间,它的价值为\(V_i\).我们希望从中选择一些软件安装到 ...
- [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 ...
随机推荐
- Exchange 2013 的会议室邮箱用户一直无法正常登陆。
某客户使用了Exchange 2013 server作为邮件承载server.详细版本号为Exchange 2013 SP1. 如今客户有个需求,希望他们的邮箱作为会议室邮箱创建,并且必须有普通邮箱全 ...
- nyoj860 又见01背包(背包变形)
题目860 pid=860" style="text-decoration:none; color:rgb(55,119,188)">题目信息 执行结果 本题排行 ...
- 辨异 —— Java 中 String 的相等性比较
How do I compare strings in Java? 1. 语法知识 ==:判断的是引用的相等性(reference equality),也即是否为同一对象: .equals():判断的 ...
- hihocoder 1671 反转子串
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个只包含括号和小写字母的字符串S,例如S="a(bc(de)fg)hijk". 其中括号表示将里 ...
- 【POJ 2449】 Remmarguts' Date
[题目链接] http://poj.org/problem?id=2449 [算法] A*(启发式搜索) 首先,求第k短路可以用优先队列BFS实现,当T第k次入队时,就求得了第k短路,但是,这种做法的 ...
- B1085 [SCOI2005]骑士精神 A*搜索
其实就是一个爆搜加剪枝.直接爆搜肯定不行,而A*算法则是想假如剩下都是最优的话,我当前步数还是不足以达到这个状态,那么就直接返回,因为最优状态也无法做到显然不行. 这道题可以用A*最主要就是因为有15 ...
- B1789 Y型项链 贪心
想明白之后就是一道大水题,就是两两把最长公共前缀求出来,然后直接取最长的,然后就直接暴力算就行了... 题干: Description 欢乐岛上众多新奇的游乐项目让小可可他们玩的非常开心.现在他们正在 ...
- git使用简易指南(转)
创建新仓库 创建新文件夹,打开,然后执行 git init以创建新的 git 仓库. 检出仓库 执行如下命令以创建一个本地仓库的克隆版本:git clone /path/to/repository 如 ...
- Python 36 死锁现象和递归锁、信号量、Event事件、线程queue
一:死锁现象和递归锁 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远 ...
- LeetCode.3-最长无重复字符子串(Longest Substring Without Repeating Characters)
这是悦乐书的第341次更新,第365篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Medium级别的第2题Longest Substring Without Repeating Cha ...