分组背包……
先讲一下分组背包吧
有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的位置,然后在做分组背包的时候外层循环直接遍历这个数组。这样的话思想应该就是和普通的分组背包完全一样。分组背包上复杂度也应该是一样的。
反正现在的时间复杂度也够用了,懒得写了……