[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 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/9833241.html