zoukankan      html  css  js  c++  java
  • [tarjan][树形dp] 洛谷 P2515 软件安装

    题目描述

    现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

    但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

    我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

    输入输出格式

    输入格式:

    第1行:N, M (0<=N<=100, 0<=M<=500)

    第2行:W1, W2, ... Wi, ..., Wn (0<=Wi<=M )

    第3行:V1, V2, ..., Vi, ..., Vn (0<=Vi<=1000 )

    第4行:D1, D2, ..., Di, ..., Dn (0<=Di<=N, Di≠i )

    输出格式:

    一个整数,代表最大价值

    输入输出样例

    输入样例#1:
    3 10
    5 5 6
    2 3 4
    0 1 1
    输出样例#1:
    5

    题解

    • 首先,一个软件只能依赖一个软件,那么我们将被依赖的软件向依赖它的软件上连一条边,这么发现,每个点的入度为1
    • 每个点入度为1的图是什么,那就是树,但是这棵树有可能会形成环,然后就要打tarjan缩环
    • 设f[i][j]为以i根的树,容量为j的最大价值(不处理根),那么答案就是f[0][m]
    • 状态转移方程就很显然了,类似于在树上做一个背包,取一个点的前提条件是它的根被取了
    • f[i][j]=max(f[i][j],f[ik][j]+f[son][kw[son]]+v[son])(k为枚举的一个容量)

    代码

     1 #include <cstdio> 
     2 #include <iostream>
     3 using namespace std;
     4 struct edge { int to,from; }e[110];
     5 int n,m,cnt,tot,k,w[110],v[110],head[110],low[110],dfn[110],vis[110],sta[110],bel[110],w1[110],v1[110],s[110],g[110][110],f[110][510];
     6 void insert(int x,int y) { e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; }
     7 void tarjan(int x)
     8 {
     9     dfn[x]=low[x]=++dfn[0],sta[++sta[0]]=x,vis[x]=1;
    10     for (int i=head[x];i;i=e[i].from)
    11         if (!dfn[e[i].to]) 
    12         {
    13             tarjan(e[i].to);
    14             low[x]=min(low[x],low[e[i].to]);
    15         }
    16         else if (vis[e[i].to]) low[x]=min(low[x],dfn[e[i].to]);
    17     if (dfn[x]==low[x])
    18     {
    19         tot++;
    20         do
    21         {
    22             k=sta[sta[0]--],vis[k]=0;
    23             bel[k]=tot,w1[tot]+=w[k],v1[tot]+=v[k];
    24         }
    25         while (k!=x);
    26     }
    27 }
    28 void dfs(int x)
    29 {
    30     for (int i=1;i<=tot;i++)
    31         if (g[x][i])
    32         {
    33             dfs(i);
    34             for (int j=m;j>=w1[i];j--) for (int k=w1[i];k<=j;k++) f[x][j]=max(f[x][j],f[i][k-w1[i]]+v1[i]+f[x][j-k]);
    35         }
    36 }
    37 int main()
    38 {
    39     scanf("%d%d",&n,&m);
    40     for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    41     for (int i=1;i<=n;i++) scanf("%d",&v[i]);
    42     for (int i=1,x;i<=n;i++)
    43     {
    44         scanf("%d",&x);
    45         if (x) insert(x,i);
    46     }
    47     for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
    48     for (int i=1;i<=n;i++)
    49         for (int j=head[i];j;j=e[j].from)
    50             if (bel[i]!=bel[e[j].to]&&!g[bel[i]][bel[e[j].to]])
    51                 g[bel[i]][bel[e[j].to]]=1,s[bel[e[j].to]]++;
    52     for (int i=1;i<=n;i++) if (!s[i]) g[0][i]=1;
    53     dfs(0);
    54     printf("%d",f[0][m]);
    55 }
  • 相关阅读:
    Vue(小案例_vue+axios仿手机app)_go实现退回上一个路由
    nyoj 635 Oh, my goddess
    nyoj 587 blockhouses
    nyoj 483 Nightmare
    nyoj 592 spiral grid
    nyoj 927 The partial sum problem
    nyoj 523 亡命逃窜
    nyoj 929 密码宝盒
    nyoj 999 师傅又被妖怪抓走了
    nyoj 293 Sticks
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9833241.html
Copyright © 2011-2022 走看看