BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP
题意:
现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。
分析:
有环。
考虑一个环内的贡献
要么都取要么都不取
缩个点跑树形背包就可以了
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 350 int head[N],to[N<<1],nxt[N<<1],cnt,n; int m,f[N][600],cost[N],val[N],c[N],v[N],in[N]; int st[N],top,ins[N],bl[N],scc,tot,vis[N]; int dfn[N],low[N],from[N]; inline void add(int u,int v){ to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;from[cnt]=u; } void tarjan(int x){ st[top++]=x; ins[x]=1; low[x]=dfn[x]=++tot; for(int i=head[x];i;i=nxt[i]){ if(!dfn[to[i]]){ tarjan(to[i]); low[x]=min(low[x],low[to[i]]); }else if(ins[to[i]]){ low[x]=min(low[x],dfn[to[i]]); } } if(low[x]==dfn[x]){ int t=st[--top];ins[t]=0; bl[t]=++scc; c[scc]=cost[t]; v[scc]=val[t]; while(t^x){ t=st[--top];ins[t]=0; c[scc]+=cost[t]; v[scc]+=val[t]; bl[t]=scc; } } } void dfs(int x,int y){ f[x][c[x]]=v[x]; vis[x]=1; for(int i=head[x];i;i=nxt[i]){ if(!vis[to[i]]){ dfs(to[i],x); for(int j=m;j>=c[x];j--){ for(int k=m-j;k>=c[to[i]];k--){ f[x][j+k]=max(f[x][j+k],f[x][j]+f[to[i]][k]); } } } } } int main(){ scanf("%d%d",&n,&m); int i,j,x,y; for(i=1;i<=n;i++)scanf("%d",&cost[i]); for(i=1;i<=n;i++)scanf("%d",&val[i]); for(i=1;i<=n;i++){ scanf("%d",&x); if(x) add(x,i); } for(i=1;i<=n;i++)if(!dfn[i])tarjan(i); int tmp=cnt; memset(head,0,sizeof(head));cnt=0; for(i=1;i<=tmp;i++){ x=from[i],y=to[i]; if(bl[x]!=bl[y]){ add(bl[x],bl[y]); in[bl[y]]++; } } for(i=1;i<=scc;i++)if(!in[i]){ add(scc+1,i); } dfs(scc+1,0); int ans=0; for(i=0;i<=m;i++)ans=max(ans,f[scc+1][i]); printf("%d\n",ans); } /* 3 10 5 5 6 2 3 4 0 1 1 */