P2515 [HAOI2010] 软件安装
P2515 [HAOI2010] 软件安装
Description
我们考虑安装一些软件,软件之间有一些依赖关系,每个软件有价值 \(v_i\) 和代价 \(w_i\)。
求在空间为 \(m\) 的情况下,价值的最大值。
Solution
首先考虑缩点,因为在同一个强联通分量内的点要选则必须一起选,我们考虑维护一个 \(W_i\) 和 \(V_i\) 表示缩点后的整体代价和价值。
对于入度为 0 的点,我们考虑从 0 连一条边指向它,最后考虑树上背包即可。
设 \(f_{i,j}\) 表示对于以 \(i\) 为根的子树,考虑 \(j\) 个空间的最大价值。
转移:\(dp_{x,i+W_x}=\max(dp_{son_x,j+dp_x,i+W_x-j})\)。即在选它的情况下,给它分配 \(i\) 个空间,并且给它的某个儿子分配 \(j\) 个空间。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+7;
int a[N],w[N];
int n,m;
vector<int> G[N];
int d[N];
int dfn[N],low[N],dfn_clock;
stack<int> s;
int tot,belongs[N];
bool vis[N];
vector<int> G2[N];
int rd[N];
int dp[N][N];
int scc_w[N];
int scc_v[N];
void tarjan(int x){
dfn[x]=low[x]=++dfn_clock;vis[x]=1;s.push(x);
for(int k:G[x]){
if(!dfn[k]) tarjan(k),low[x]=min(low[x],low[k]);
else if(vis[k]) low[x]=min(low[x],dfn[k]);
}
if(dfn[x]==low[x]){
++tot;
int k=0;
while(x!=k){
k=s.top(),vis[k]=0,s.pop(),belongs[k]=tot;
scc_w[tot]+=w[k],scc_v[tot]+=a[k];
}
}
}
void solve(int x){
for(int i=scc_w[x];i<=m;i++) dp[x][i]=scc_v[x];
for(int k:G2[x]){
solve(k);int left=m-scc_w[x];
for(int i=left;i>=0;i--){
for(int j=0;j<=i;j++){
dp[x][i+scc_w[x]]=max(dp[x][i+scc_w[x]],dp[k][j]+dp[x][i+scc_w[x]-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]) G[d[i]].push_back(i);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++){
if(belongs[d[i]]!=belongs[i]){
G2[belongs[d[i]]].push_back(belongs[i]);
rd[belongs[i]]++;
}
}
for(int i=1;i<=tot;i++) if(!rd[i]) G2[0].push_back(i);
solve(0);
printf("%d",dp[0][m]);
return 0;
}