题目描述
现在我们的手头有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
- 每个点入度为1的图是什么,那就是树,但是这棵树有可能会形成环,然后就要打tarjan缩环
- 设f[i][j]为以i根的树,容量为j的最大价值(不处理根),那么答案就是f[0][m]
- 状态转移方程就很显然了,类似于在树上做一个背包,取一个点的前提条件是它的根被取了
- f[i][j]=max(f[i][j],f[i−k][j]+f[son][k−w[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 }