网上对这题一致好评,然而像我这种没见过世面的,不知道什么是好题,什么题不好......
数据会出现环形依赖关系,环形里面的软件选一个就要全选,相当于一件物品。
所以先跑Tarjan,把强连通分量缩成一个点,之后按强连通分量建图,原图 u->v,新边 scc[v]->scc[u],表示安装scc[v]以后才能装scc[u]。
之后是树形背包。
这是我的第一道树形背包。(我的题解顺序不代表写题顺序,我的题解是补得)
很没有经验。
第一次是按我自己简单的思路走的,只得了30。
原因是在子树空间分配上出了问题,导致在我的dfs过程中丢失了很多状态。
f[u][j] 表示给以 u 为根的子树分配 j 空间的最优值。
要枚举每一个状态。
第一维要枚举 j,第二维枚举 k,意义在于:当以 u 为根的子树有 j 空间时,给 u 的子树 v 分配 j-k 空间。
f[u][j] = max ( f[u][j] , f[u][k] + f[v][j-k] )
上述过程并没有把根节点的贡献算进去。
所以之后要一边循环把根节点的贡献算上。
当状态成立当且仅当 f[u][j] 的 j>=w[u],此时 f[u][j] = f[u-w[u]] + val[u] 。
// q.c
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
const int N=100+10,M=500+10;
int n,m,_weight[N],weight[N],_value[N],value[N],f[N][M],cnt,head[N];
struct Edge {
int u,v,nex;
Edge():u(0),v(0),nex(-1) {}
}ed[N];
void add_edge(int a,int b) {
ed[++cnt].u=a,ed[cnt].v=b,ed[cnt].nex=head[a],head[a]=cnt;
}
int dfs_clock,scc_cnt,pre[N],low[N],idx[N],in[N],g[N][N];
stack<int> s;
void find_scc(int u) {
pre[u]=low[u]=++dfs_clock;
s.push(u);
for(int i=head[u];i!=-1;i=ed[i].nex) {
Edge e=ed[i];
if(!pre[e.v]) {
find_scc(e.v);
low[u]=min(low[u],low[e.v]);
}else if(!idx[e.v])
low[u]=min(low[u],pre[e.v]);
}
if(low[u]==pre[u]) {
++scc_cnt;
for(;;) {
int v=s.top(); s.pop();
idx[v]=scc_cnt;
if(v==u) break;
}
}
}
void dfs(int u) { // f[u][k]给以u为根的子树分配k空间的最优值(含根).
for(int i=1;i<=scc_cnt;i++) if(g[u][i]) {
dfs(i);
for(int j=m;j>=0;j--) // 这两个循环相当于给u的子树分配空间.
for(int k=0;k<=j;k++)
f[u][j]=max(f[u][j],f[u][k]+f[i][j-k]);
}
for(int j=m;j>=0;j--) { // 在这一步把当前节点(即根节点)的贡献加进去.
if(j>=weight[u]) f[u][j]=f[u][j-weight[u]]+value[u];
else f[u][j]=0;
}
}
void input() {
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m); int x;
for(int i=1;i<=n;i++) scanf("%d",&_weight[i]);
for(int i=1;i<=n;i++) scanf("%d",&_value[i]);
for(int i=1;i<=n;i++) { scanf("%d",&x); if(x) add_edge(i,x); }
}
void prepare() {
for(int i=1;i<=n;i++) if(!pre[i]) find_scc(i);
for(int i=1;i<=n;i++) {
weight[idx[i]]+=_weight[i];
value[idx[i]]+=_value[i];
for(int j=head[i];j!=-1;j=ed[i].nex) {
Edge e=ed[j];
if(idx[e.u]!=idx[e.v]) {
g[idx[e.v]][idx[e.u]]=true;
in[idx[e.u]]++;
}
}
}
for(int i=1;i<=scc_cnt;i++) if(!in[i]) g[0][i]=true;
}
int main() {
freopen("install.in","r",stdin);
freopen("install.out","w",stdout);
input();
prepare();
dfs(0);
printf("%d\n",f[0][m]);
return 0;
}