[HAOI2010]软件安装

  网上对这题一致好评,然而像我这种没见过世面的,不知道什么是好题,什么题不好......

  数据会出现环形依赖关系,环形里面的软件选一个就要全选,相当于一件物品。

  所以先跑Tarjan,把强连通分量缩成一个点,之后按强连通分量建图,原图 u->v,新边 scc[v]->scc[u],表示安装scc[v]以后才能装scc[u]。

  之后是树形背包。

  这是我的第一道树形背包。(我的题解顺序不代表写题顺序,我的题解是补得)

  很没有经验。

  第一次是按我自己简单的思路走的,只得了30。

  原因是在子树空间分配上出了问题,导致在我的dfs过程中丢失了很多状态。

  f[u][j] 表示给以 u 为根的子树分配  j  空间的最优值。

  要枚举每一个状态。

  第一维要枚举 j,第二维枚举 k,意义在于:当以 u 为根的子树有 j 空间时,给 u 的子树 v 分配 j-k 空间。

  f[u][j] = max ( f[u][j] , f[u][k] + f[v][j-k] )

  上述过程并没有把根节点的贡献算进去。

  所以之后要一边循环把根节点的贡献算上。

  当状态成立当且仅当 f[u][j] 的 j>=w[u],此时 f[u][j] = f[u-w[u]] + val[u] 。

 

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<stack>
using namespace std;
const int N=100+10,M=500+10;
int n,m,_weight[N],weight[N],_value[N],value[N],f[N][M],cnt,head[N];
struct Edge {
	int u,v,nex;
	Edge():u(0),v(0),nex(-1) {}
}ed[N];
void add_edge(int a,int b) {
	ed[++cnt].u=a,ed[cnt].v=b,ed[cnt].nex=head[a],head[a]=cnt;
}
int dfs_clock,scc_cnt,pre[N],low[N],idx[N],in[N],g[N][N];
stack<int> s;
void find_scc(int u) {
	pre[u]=low[u]=++dfs_clock;
	s.push(u);
	for(int i=head[u];i!=-1;i=ed[i].nex) {
		Edge e=ed[i];
		if(!pre[e.v]) {
			find_scc(e.v);
			low[u]=min(low[u],low[e.v]);
		}else if(!idx[e.v]) 
			low[u]=min(low[u],pre[e.v]);
	}
	if(low[u]==pre[u]) {
		++scc_cnt;
		for(;;) {
			int v=s.top(); s.pop();
			idx[v]=scc_cnt;
			if(v==u) break;
		}
	}
}
void dfs(int u) { // f[u][k]给以u为根的子树分配k空间的最优值(含根).
	for(int i=1;i<=scc_cnt;i++) if(g[u][i]) {
		dfs(i);
		for(int j=m;j>=0;j--) // 这两个循环相当于给u的子树分配空间.
			for(int k=0;k<=j;k++)
				f[u][j]=max(f[u][j],f[u][k]+f[i][j-k]);
	}
	for(int j=m;j>=0;j--) { // 在这一步把当前节点(即根节点)的贡献加进去.
		if(j>=weight[u]) f[u][j]=f[u][j-weight[u]]+value[u];
		else f[u][j]=0;
	}
}
void input() {
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m); int x;
	for(int i=1;i<=n;i++) scanf("%d",&_weight[i]);
	for(int i=1;i<=n;i++) scanf("%d",&_value[i]);
	for(int i=1;i<=n;i++) { scanf("%d",&x); if(x) add_edge(i,x); }
}
void prepare() {
	for(int i=1;i<=n;i++) if(!pre[i]) find_scc(i);
	for(int i=1;i<=n;i++) {
		weight[idx[i]]+=_weight[i];
		value[idx[i]]+=_value[i];
		for(int j=head[i];j!=-1;j=ed[i].nex) {
			Edge e=ed[j];
			if(idx[e.u]!=idx[e.v]) {
				g[idx[e.v]][idx[e.u]]=true;
				in[idx[e.u]]++;
			}
		}
	}
	for(int i=1;i<=scc_cnt;i++) if(!in[i]) g[0][i]=true;
}
int main() {
	freopen("install.in","r",stdin);
	freopen("install.out","w",stdout);
	input();
	prepare();
	dfs(0);
	printf("%d\n",f[0][m]);
	return 0;
}

 

posted @ 2018-04-15 14:44  qjs12  阅读(145)  评论(0编辑  收藏  举报