题解:Luogu P2515 [HAOI2010]软件安装

题面

本文主要来记录一个没见过的树形背包 DP 写法

这题是个裸的 tarjan 缩点和树形背包 DP

显然循环依赖就缩成一点再进行 DP 即可

(f(i,j)) 为 dfs 序中第 i 个点及以后占用磁盘空间为 j 的连通块的最大价值

(f(i,j)=max {f(i+1,j-W[i])+V[i],f(i+size_{dfn_i},j)})

时间复杂度 (mathcal{O}(n imes m))

Code(C++):

#include<bits/stdc++.h>
#define forn(i,s,t) for(int i=(s);i<=(t);++i)
#define form(i,s,t) for(int i=(s);i>=(t);--i)
using namespace std;
const int N = 105,M = 505,INF = 1e9+7;
template<typename T>
inline void chkmax(T &A,T B) {A=A>B?A:B;}
struct List {
	int dir,nxt;
}E[N],T[N];
int G[N],cnt,cntt,GT[N],dT[N];
inline void Add(int u,int v) {
	E[++cnt].dir = v,E[cnt].nxt = G[u],G[u] = cnt;
}
inline void AddT(int u,int v) {
	T[++cntt].dir = v,T[cntt].nxt = GT[u],GT[u] = cntt,dT[v] ++ ;
}
int n,m,w[N],v[N],d[N],dn[N],stk[N],h,ord,clr[N],col,W[N],V[N];
bool vis[N];
int tarjan(int u) {
	int low = dn[u] = ++ord;
	stk[++h] = u,vis[u] = 1;
	for(int i=G[u];i;i=E[i].nxt) {
		int v = E[i].dir;
		if(!dn[v]) low = min(tarjan(v),low);
		else if(vis[v]) low = min(low,dn[v]);
	}
	if(low == dn[u]) {
		++col;
		do clr[stk[h]]=col,W[col]+=w[stk[h]],V[col]+=v[stk[h]],vis[stk[h]]=0;
		while(stk[h--]!=u);
	}
	return low;
}
int f[N][M],sz[N],rk[N];
void dfs(int u) {
	sz[u] = 1,rk[++ord] = u;
	for(int i=GT[u];i;i=T[i].nxt) {
		int v = T[i].dir;
		dfs(v),sz[u] += sz[v];
	}
}
int main() {
	scanf("%d%d",&n,&m);
	forn(i,1,n) scanf("%d",&w[i]);
	forn(i,1,n) scanf("%d",&v[i]);
	forn(i,1,n) scanf("%d",&d[i]),d[i]?(Add(d[i],i),0):0;
	forn(i,1,n) if(!dn[i]) tarjan(i);
	forn(u,1,n) for(int i=G[u];i;i=E[i].nxt) {
		int v = E[i].dir;
		if(clr[u]!=clr[v]) AddT(clr[u],clr[v]);
	}
	forn(u,1,col) if(!dT[u]) AddT(0,u);
	ord = 0,dfs(0);
	form(i,ord,1) { int u = rk[i];
		forn(j,0,m) {
			chkmax(f[i][j],f[i+sz[u]][j]);
			if(j>=W[u]) chkmax(f[i][j],f[i+1][j-W[u]]+V[u]);
		}
	}
	printf("%d
",f[1][m]);
	return 0;
}
原文地址:https://www.cnblogs.com/Ax-Dea/p/14077032.html