【Luogu_P2515】软件安装

41 篇文章 0 订阅
21 篇文章 0 订阅
链接

题目大意:

就是让你做有依赖的背包问题

思路:

1.

首先我们思考出现环的情况:
如果出现了环,也就是说它有两种可能:1是这个环里的数全不选,2是全选。于是我们就可以把它压缩成一个点,具体方式如下:

  • 点i所在的环之前没有判断过,是新环。我们将这个环放到数组最后,新加一个点,储存这个环,然后让这两个点的空间标记为负值tmpw,且tmpw+tmpn(新点的下标)等于原来的坐标,我们就可以通过这个坐标找到这个环所在的点。
  • 2、点i所在的环之前已经判断过了,是旧环,且i是环的一部分(i还没合并)。那么我们就把i也加到这个环里面,让环的体积,价值相加即可;
  • 3、点j所在的环是旧环,但是i不是环的一部分(例如1依赖2,2依赖3,3依赖1。4也依赖1,那么,4所在的是个环,但4不属于环的一部分)。那么,把j的父亲转到环上。

于是,我们就有:

void VL_merge()
{
   tmpn=n;
   for(int i=1; i<=tmpn; i++)
   	for(int j=1; j<=tmpn; j++)
   	{
   		if(a[i][j]==1&&a[j][i]==1&&w[i]>0&&w[j]>0&&i!=j)
   		{
   			tmpn++;
   			w[tmpn]=w[i]+w[j];
   			v[tmpn]=v[i]+v[j];
   			tmpw--;
   			w[i]=tmpw, w[j]=tmpw;
   		}
   		if(a[d[j]][j]==1&&a[j][d[j]]==1&&w[d[j]]<0&&w[j]>0)
   		{
   			w[n-w[d[j]]]+=w[j];
   			v[n-w[d[j]]]+=v[j];
   			w[j]=w[d[j]];
   		}
   		if(w[d[j]]<0&&w[j]>0)
   			if((a[d[j]][j]==1&&a[j][d[j]]==0)||(a[d[j]][j]==0&&a[j][d[j]]==1))
   				d[j]=n-w[d[j]];
   	}
}
2.

我们分析x和它儿子和它爸爸的关系

  • 它要在它爸爸选了之后才能选,所以它爸爸的值可以传承到它身上,当作不选它
  • 我们类似完全背包的做法,枚举当前还剩的空间,然后可以得出
		f[c[x]][y-i]=VL_dfs(c[x], y-i);//枚举儿子的空间
		f[b[x]][i]=VL_dfs(b[x], i);//作为它父亲的前传
		f[x][k]=max(f[x][k], v[x]+f[c[x]][y-i]+f[b[x]][i]);

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
const int MAXN=1000;

int n, m, tmpw, tmpn, w[MAXN], v[MAXN], d[MAXN], c[MAXN], b[MAXN];
int a[MAXN][MAXN];
int f[MAXN][MAXN];

void VL_floyed()
{
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			for(int k=1; k<=n; k++)
				if(a[k][i]==1&&a[i][j]==1)
					a[k][j]=1;//Floyde判断环
}
void VL_merge()
{
	tmpn=n;
	for(int i=1; i<=tmpn; i++)
    	for(int j=1; j<=tmpn; j++)
		{
			if(a[i][j]==1&&a[j][i]==1&&w[i]>0&&w[j]>0&&i!=j)
			{
				tmpn++;
				w[tmpn]=w[i]+w[j];
				v[tmpn]=v[i]+v[j];
				tmpw--;
				w[i]=tmpw, w[j]=tmpw;
			}
			if(a[d[j]][j]==1&&a[j][d[j]]==1&&w[d[j]]<0&&w[j]>0)
			{
				w[n-w[d[j]]]+=w[j];
				v[n-w[d[j]]]+=v[j];
				w[j]=w[d[j]];
			}
			if(w[d[j]]<0&&w[j]>0)
				if((a[d[j]][j]==1&&a[j][d[j]]==0)||(a[d[j]][j]==0&&a[j][d[j]]==1))
					d[j]=n-w[d[j]];
		}
}
int VL_dfs(int x, int k)
{
	if(f[x][k]>0)
		return f[x][k];
	if(x==0||k<=0)
		return 0;
	f[b[x]][k]=VL_dfs(b[x], k);
	f[x][k]=f[b[x]][k];
	int y=k-w[x];
	for(int i=0; i<=y; i++)
	{
		f[c[x]][y-i]=VL_dfs(c[x], y-i);
		f[b[x]][i]=VL_dfs(b[x], i);
		f[x][k]=max(f[x][k], v[x]+f[c[x]][y-i]+f[b[x]][i]);
	}
	return f[x][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]);
		a[d[i]][i]=1;
	}
	VL_floyed();
	VL_merge();
	for(int i=1; i<=tmpn; i++)
	{
		if(w[i]>0)
		{
			b[i]=c[d[i]];
			c[d[i]]=i;
		}
	}
	printf("%d", VL_dfs(c[0], m));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值