[BZOJ 2427] 软件安装
Link:
Solution:
只看样例的话会以为是裸的树形$dp$……
但实际上题目并没有说明恰好仅有一个物品没有依赖项
因此原图可能由是由多棵树与多个图组成的
先跑一遍$tarjan$求出每个图中的$SCC$,缩点将原图转化为森林
再设置一个根,将森林转换成一棵树$dp$即可:$dp[i][j]=max\{ dp[i][k]+dp[son[i]][j-k]\}$
为了保证依赖条件满足,每棵子树的根都必须要选择,因此返回前还要再刷一遍:$dp[i][j]=dp[i][j-w_i]+v_i$
Tip:$dp$时要注意$k$也要逆序枚举,因为$w_i$可能为0
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=505; struct edge{int nxt,to;}e[MAXN<<2]; stack<int> st; int f[MAXN],head[MAXN],in[MAXN],S; int instack[MAXN],vis[MAXN],col[MAXN],dfn[MAXN],low[MAXN]; int n,m,x,dp[MAXN][MAXN],w[MAXN],v[MAXN],sw[MAXN],sv[MAXN],tot,cnt,idx; void add_edge(int from,int to) {e[++tot].nxt=head[from];e[tot].to=to;head[from]=tot;} void tarjan(int x) { dfn[x]=low[x]=++idx; instack[x]=vis[x]=true;st.push(x); for(int i=head[x];i;i=e[i].nxt) { if(!vis[e[i].to]) tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]); else if(instack[e[i].to]) low[x]=min(low[x],low[e[i].to]); } if(dfn[x]==low[x]) { int t=-1;cnt++; while(t!=x) { t=st.top();st.pop(); instack[t]=false;col[t]=cnt; sw[cnt]+=w[t];sv[cnt]+=v[t]; } } } void dfs(int x,int anc) { for(int i=head[x];i;i=e[i].nxt) { if(e[i].to==anc) continue; dfs(e[i].to,x); for(int j=m-sw[x];j>=0;j--) for(int k=j;k>=0;k--)//k也要保持逆序 dp[x][j]=max(dp[x][j],dp[x][k]+dp[e[i].to][j-k]); } for(int j=m;j>=0;j--) if(j>=sw[x]) dp[x][j]=dp[x][j-sw[x]]+sv[x]; else dp[x][j]=0; } 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",&v[i]); for(int i=1;i<=n;i++) { scanf("%d",&f[i]); if(f[i]) add_edge(f[i],i); } for(int i=1;i<=n;i++)//原图不一定连通 if(!vis[i]) tarjan(i); tot=0;S=cnt+1; memset(head,0,sizeof(head)); for(int i=1;i<=n;i++) if(f[i]&&col[i]!=col[f[i]]) add_edge(col[f[i]],col[i]),in[col[i]]++; for(int i=1;i<=cnt;i++)//建立根 if(!in[i]) add_edge(S,i); dfs(S,0); printf("%d",dp[S][m]); return 0; }