洛谷P2515 [HAOI2010]软件安装 (树形背包+tarjan缩点)

[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
*/
View Code
原文地址:https://www.cnblogs.com/mowanying/p/11656382.html