题目描述
输入格式
输出格式
样例
solution
注意一下这个,这道题没有说不能有环。。。所以我们要用到简单易懂(够你调一年)的tarjan缩点。
环的情况是存在的,如果存在一个依赖的环,我们要想安装其中一个软件的话就是把环上所有软件都一起打包安装(其实就是缩点)(不要跟我说你不能同时安装几个软件)
刚看到这题就想起了金明那道题:http://www.luogu.org/problem/show?pid=1064#
但这题还是有些不一样的
一开始想用dfs序,但始终做不出来,还是乖乖去搞树形dp了
我们先用tarjan找强联通分量,处理出新的w和v
之后在用求出来的强联通分量建图。建出来的图可能会是一片森林,所以我们搞一个虚拟的点,连接所有树的根节点;
这样就可以愉快地在新树上做树形依赖背包了,f[i][j]表示以i为根节点的子树占用j的空间所能得到的最大价值,目标f[root][M]。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; const int MAXN=105; const int MAXM=505; int n,m,w[MAXN],v[MAXN],d[MAXN]; int ver[MAXN],first[MAXN],head[MAXN],cnt=0; void add(int u,int v){ cnt++,ver[cnt]=v,head[cnt]=first[u],first[u]=cnt; } int dfn[MAXN],low[MAXN],belong[MAXN],dfs_num=0,stack[MAXN],top=0; int W[MAXN],V[MAXN],tot=0; bool in_stack[MAXN]; void tarjan(int x){ dfn[x]=low[x]=++dfs_num; in_stack[x]=1; stack[++top]=x; for(int i=first[x];i;i=head[i]){ int v=ver[i]; if(!dfn[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(in_stack[v]){ low[x]=min(low[x],dfn[v]); } } if(dfn[x]==low[x]){ tot++; int y; do{ y=stack[top--]; in_stack[y]=0; belong[y]=tot; W[tot]+=w[y]; V[tot]+=v[y]; }while(y!=x); } } int to[MAXN],pre[MAXN],nxt[MAXN],degree_in[MAXN]; void ADD(int u,int v){ cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt; } int f[MAXN][MAXM]; void dp(int x){ for(int i=0;i<=m;i++){ if(i<W[x]) f[x][i]=0; else f[x][i]=V[x]; } for(int i=pre[x];i;i=nxt[i]){ int y=to[i]; dp(y); for(int j=m;j>=W[x];j--){ for(int p=0;p<=j-W[x];p++){ f[x][j]=max(f[x][j],f[y][p]+f[x][j-p]); } } } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&w[i]); for(int i=1;i<=n;i++) scanf("%d",&v[i]); for(int i=1;i<=n;i++){ scanf("%d",&d[i]); if(d[i]!=0) add(d[i],i); } for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i); } cnt=0; for(int i=1;i<=n;i++){ for(int j=first[i];j;j=head[j]){ int v=ver[j]; if(belong[i]!=belong[v]){ ADD(belong[i],belong[v]); degree_in[belong[v]]++; } } } for(int i=1;i<=tot;i++){ if(!degree_in[i]) ADD(tot+1,i); } dp(++tot); printf("%d ",f[tot][m]); return 0; }