【Luogu P2515】软件安装

Luogu P2515
这道题的题面与P2146有点像。一些不同地方就是P2146是无环的,这题是有环的。
很显然,如果有几个软件的依赖关系形成环,那么这几个软件就可以被看成是一个大软件,其价值和空间都是原先的总和。
那么,我们就可以利用Tarjan算法求强连通分量+缩点,最后加一个树上的背包就可以了。
注意,缩点后的图不一定是一棵树,但是我们可以人为的加入一个权值为零的根节点,连接所有入度为0的点。
有关Tarjan算法和强连通分量:
【Luogu P3387】缩点模板(强连通分量Tarjan&拓扑排序)

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=110,maxm=510;
int dfn[maxn],low[maxn],stk[maxn],cnt,tim,d[maxn],tot,sccw[maxn],sccv[maxn];
int w[maxn],v[maxn],scc[maxn],in[maxn],ohead[maxn],ocnt,m,n,head[maxn],dp[maxn][maxm],ans;
struct data
{
	int to,next;
}oe[maxn],e[maxn];
bool vis[maxn];
void Tarjan(int now)
{
	dfn[now]=low[now]=++tim;
	stk[++cnt]=now;
	vis[now]=true;
	for (int i=head[now];i;i=e[i].next)
	{ 
		if (!dfn[e[i].to])
		{
			Tarjan(e[i].to);
			low[now]=min(low[now],low[e[i].to]);
		}
		else
		{
			if (vis[e[i].to]) low[now]=min(low[now],dfn[e[i].to]);
		}
	}
	if (dfn[now]==low[now])
	{
		tot++;
		while (true)
		{
			scc[stk[cnt]]=tot;
			sccv[tot]+=v[stk[cnt]];
			sccw[tot]+=w[stk[cnt]];
			vis[stk[cnt]]=false;
			cnt--;
			if (stk[cnt+1]==now) break;
		}
	}
}
void dfs(int now)//树上背包
{
	//基本思路如下:枚举子树能取的空间,再利用01背包的原理进行状态转移。
	//还有一种方法可以把多叉树转化成二叉树,按照左儿子又兄弟的原则重新建树。实现起来比较麻烦,所以没写。
	for (int i=sccw[now];i<=m;i++) dp[now][i]=sccv[now];//初始值
	for (int i=ohead[now];i;i=oe[i].next)
	{
		int to=oe[i].to;
		dfs(to);
		for (int j=m-sccw[now];j>=0;j--) 
		{
			for (int k=0;k<=j;k++)
				dp[now][j+sccw[now]]=max(dp[now][j+sccw[now]],dp[now][j+sccw[now]-k]+dp[to][k]);
		}
	}
}
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]);
		e[i].to=i;
		e[i].next=head[d[i]];
		head[d[i]]=i;
		//注意建边的方向。
	}
	cnt=0;
	for (int i=1;i<=n;i++) if (!dfn[i]) Tarjan(i);
	ocnt=0;
	for (int i=1;i<=n;i++)
		for (int j=head[i];j;j=e[j].next)
		if (scc[i]!=scc[e[j].to])
		{
			oe[++ocnt].to=scc[e[j].to];
			oe[ocnt].next=ohead[scc[i]];
			ohead[scc[i]]=ocnt;
			in[scc[e[j].to]]++;//统计入度
		}
	tot++;//人为加入的根节点
	for (int i=1;i<tot;i++)
		if (in[i]==0)
		{
			oe[++ocnt].to=i;
			oe[ocnt].next=ohead[tot];
			ohead[tot]=ocnt;//连边。
		}
	dfs(tot);
	printf("%d\n",dp[tot][m]);
	return 0;
}
posted @ 2019-11-11 15:21  Nanjo  阅读(125)  评论(0)    收藏  举报