【传送门:BZOJ2427】
简要题意:
给出n个软件,共有m的空间,每个软件只能安装一次,每个软件安装后占用w[i]的空间,得到v[i]的价值
有些软件至多有一个它们的父软件,相当于父软件被子软件依赖,只有当父软件被安装,才能安装当前的软件
求出最大价值
题解:
如果x为y的父软件,则x连向y
很显然会出现多个软件互相依赖的情况,就是成环了
那么就用强联通缩点,然后将空间和费用累加,作为新点
然后如果没有父软件的点,设一个虚根0,然后0连向这些点
设f[i][j]为在i点时使用j的空间的最大价值
然后在树形DP的时候,在继承的时候保证父节点被选中才能选子节点就可以了
参考代码:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> using namespace std; int w[110],v[110]; struct node { int x,y,next; }a[110],b[110];int len,last[110]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int dfn[110],low[110],id; int belong[110],s; int sta[110],tp; bool bo[110]; int V[110],W[110]; void dfs(int x) { dfn[x]=low[x]=++id; sta[++tp]=x;bo[x]=true; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]==-1) { dfs(y); low[x]=min(low[x],low[y]); } else { if(bo[y]==true) low[x]=min(low[x],dfn[y]); } } if(low[x]==dfn[x]) { s++;int i; do { i=sta[tp--]; belong[i]=s; bo[i]=false; }while(i!=x); } } int f[110][510]; int m; void treedp(int x) { f[x][W[x]]=V[x]; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; treedp(y); for(int i=m;i>W[x];i--) { for(int j=0;j<=i-W[x];j++) { if(i>=j) f[x][i]=max(f[x][i],f[y][j]+f[x][i-j]); } } } } int d[110],ru[110]; int main() {int n; 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]); len=0;memset(last,0,sizeof(last)); for(int i=1;i<=n;i++) { scanf("%d",&d[i]); ins(d[i],i); } memset(bo,false,sizeof(bo)); id=s=tp=0; memset(V,0,sizeof(V)); memset(W,0,sizeof(W)); memset(dfn,-1,sizeof(dfn)); for(int i=1;i<=n;i++) if(dfn[i]==-1) dfs(i); len=0;memset(last,0,sizeof(last)); memset(ru,0,sizeof(ru)); for(int i=1;i<=n;i++) { int x=d[i],y=i; V[belong[i]]+=v[i];W[belong[i]]+=w[i]; if(belong[x]!=belong[y]) { ins(belong[x],belong[y]); ru[belong[y]]++; } } for(int i=1;i<=s;i++) if(ru[i]==0) ins(0,i); treedp(0); printf("%d\n",f[0][m]); return 0; }