[HAOI2010]软件安装
分析:
每个点最多依赖一个点,最后建出来的图可能成环,也可能是一棵树。
先用tarjan缩点,对缩点后的图建边,会建成一颗森林(有成环点缩点后孤立)
用一个超级源点向入度为0的点连边,跑一遍树形dp
注意:
1. 弄清楚依赖关系
2. 树形dp中要固定选父节点
#include<bits/stdc++.h> using namespace std; #define N 105 #define ri register int int to[N],nex[N],head[N],tot=0,dp[N][N*5],flag[N],n,m,ru[N]; int dfn[N],low[N],stk[N],Ti=0,cnt=0,bel[N],w1[N],v1[N],w2[N],v2[N],top=0; vector<int> e[N]; vector<int> scc[N]; void add(int a,int b) { to[++tot]=b; nex[tot]=head[a]; head[a]=tot; } void tarjan(int x) { dfn[x]=low[x]=++Ti; stk[++top]=x; flag[x]=true; for(ri i=head[x];i;i=nex[i]){ int v=to[i]; if(!dfn[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(flag[v]) low[x]=min(low[x],dfn[v]); } if(low[x]==dfn[x]){ cnt++; do{ int tmp=stk[top]; scc[cnt].push_back(tmp); flag[tmp]=0; bel[tmp]=cnt; }while(stk[top--]!=x); } } void dfs(int u) { for(ri i=v2[u];i<=m;++i) dp[u][i]=w2[u]; for(ri i=0;i<e[u].size();++i){ int v=e[u][i]; dfs(v); for(ri j=m;j>=v2[u];--j) for(ri k=0;k<=j-v2[u];++k)//j-v2[u]固定了一定会选到父节点!! dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]); } } int main() { int d; scanf("%d%d",&n,&m); for(ri i=1;i<=n;++i) scanf("%d",&v1[i]); for(ri i=1;i<=n;++i) scanf("%d",&w1[i]); for(ri i=1;i<=n;++i) { scanf("%d",&d); if(d) add(d,i); } for(ri i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(ri i=1;i<=n;++i){ w2[bel[i]]+=w1[i]; v2[bel[i]]+=v1[i]; for(ri j=head[i];j;j=nex[j]){ int v=to[j]; if(bel[i]!=bel[v]) e[bel[i]].push_back(bel[v]),ru[bel[v]]++; } } for(ri i=1;i<=cnt;++i) if(!ru[i]) e[cnt+1].push_back(i); dfs(cnt+1); printf("%d",dp[cnt+1][m]);/**/ } /* 3 10 5 5 6 2 3 4 0 1 1 */