vijos1250 最勇敢的机器人

来源:互联网 发布:江苏普通发票软件下载 编辑:程序博客网 时间:2024/05/01 03:08

分组背包……


先讲一下分组背包吧

有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]) {//是不是一个组的fatherfor(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的位置,然后在做分组背包的时候外层循环直接遍历这个数组。这样的话思想应该就是和普通的分组背包完全一样。分组背包上复杂度也应该是一样的。

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

原创粉丝点击