vijos1250 最勇敢的机器人

分组背包……


先讲一下分组背包吧

有n件物品,分为k组,每组组内互相冲突,只能拿一件。还是放到一个容量为m的背包,每个物品有一个v[i]价值,c[i]重量。目标就是求价值最大。

这道题和普通背包的区别就只有每组只能拿一件这个条件,所以我们就可以从第一组开始遍历,然后枚举每组拿哪个。

有f[k][j]为前k组,重量为j的最大价值。

有:f[k][j]=f[k-1][j-c[i]]+v[i]; i为第k组的一件物品。

那么我们就可以用实际上还是O(nm)的复杂度求出答案。


分组背包是不是简单愉快呢。我们还是回到原题。

这道题因为有传递性这个性质(如果a-b,b-c,那么a-c),我们对数据的处理其实只需要把他们分组,变成分组背包的形式,所以我们只需要用到并查集这个数据结构。

我们用h[i][0]记录i是不是一个组的father,如果是,那么记录这个组里有几个成员。

h[i][j]  j!=0记录组里的成员。


#include<bits/stdc++.h>
#define ll long long
#define st string
#define mem(x) memset(x,0,sizeof(x))

using namespace std;

const int INF=1e9;
int a[1005];
int b[1005];
int f[1005];
int father[1005];

int find(int x)
{
	if(father[x]==x) return x;
	return father[x]=find(father[x]);
}

int h[1005][1005];

int main()
{
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	mem(h);
	mem(f);
	mem(a);
	mem(b);
	mem(father);
	int n,m,k;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
	for(int i=1;i<=n;i++) father[i]=i;
	for(int i=1;i<=k;i++) {			//初始化并查集
		int x,y;
		scanf("%d%d",&x,&y);
		x=find(x);
		y=find(y);
		father[x]=y;
	}
	for(int i=1;i<=n;i++) {			//初始化每个组
		int x=find(i);
		h[x][0]++;
		h[x][h[x][0]]=i;
	}
	for(int i=1;i<=n;i++) {			//分组背包
		if(h[i][0]) {			//是不是一个组的father
			for(int j=m;j>=0;j--) 
        		for(int k=1;k<=h[i][0];k++) {
            		if(j>=b[h[i][k]]) f[j]=max(f[j],f[j-b[h[i][k]]]+a[h[i][k]]);
        	}
		}
	}
	printf("%d\n",f[m]);
	return 0;
}

我们这道题其实还可以再开一个数组,记录每个组的father的位置,然后在做分组背包的时候外层循环直接遍历这个数组。这样的话思想应该就是和普通的分组背包完全一样。分组背包上复杂度也应该是一样的。

反正现在的时间复杂度也够用了,懒得写了……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值