搭配购买(DP+并查集)

来源:互联网 发布:华农c语言实验答案 编辑:程序博客网 时间:2024/05/17 23:06

P1455 搭配购买
题目概述:给定一组物品,每个物品对应一个价值和价格,而且定义关系G(U,V)对于一对物品ui,vi,有买ui就必须买vi的要求,同理,如果买vi就必须买ui。试求在自己的支付能力下,可以获得的最大价值。
数据规模:n<=10000,0<=m<=5000,w<=10000
输入格式:第1行,物品数N,关系数M,最大支付能力(背包规模)K。
接下来的N行,每一行都有两个数,分别表示价格PRICE和价值VALUE。
接下来的M行,每一行也是两个数,表示标号分别为U,V的物品存在关系G(U,V),关系解释见题述。
输出格式:一个int型正整数,表示自己的支付能力下,可以获得的最大价值ANS。
思路:
这个题比较显然的一个做法就是并查集+DP,经典的并查集和经典的背包DP,不过为了减少时间,我们要提前预处理一下,首先我们要知道并查集一轮处理完之后,它的FA[]数组并不是真正的根节点数组,因为缩边的时候并不是真正的根节点,对于这样的一个伪根节点数组要进行下一步处理,用一个最差情况为O(N^2)的时间来重新运行一遍findd(i)操作,这样就可以大幅减少时间,这样在运算的时候就只需要直接调用FA[]即可(话说这个小技巧在学的时候就看出来了,但是感觉对于小规模数据有点耗时间)。
代码:

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int i,j,m,n,w,temp;int c[10001],d[10001];int fa[10001],b[10001];int u[10001],v[10001];int f[10001];int r(){    int ans=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9')    {        if(ch=='-')        f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9')    {        ans*=10;        ans+=ch-'0';        ch=getchar();    }    return ans*f;}int findd(int x){    if(fa[x]==x) return x;    fa[x]=findd(fa[x]);    return fa[x];}void unionn(int x,int y){    int fx=findd(x),fy=findd(y);    if(fx==fy) return;    fa[fx]=fy;}int main(){       n=r(),m=r(),w=r();    for(i=1;i<=n;i++)    {        c[i]=r(),d[i]=r(),fa[i]=i;    }    int x,y;    for(i=1;i<=m;i++)    {        x=r(),y=r();        unionn(x,y);    }    for(i=1;i<=n;i++)    findd(i);    for(i=1;i<=n;i++)    {        if(!b[i])        {            ++temp;            //int f1=findd(i);            for(j=1;j<=n;j++)            {            //  if(findd(j)==f1&&!b[j])                if(fa[j]==fa[i]&&!b[j])                b[j]=1,u[temp]+=c[j],v[temp]+=d[j];            }        }    }    for(i=1;i<=temp;i++)    for(int vv=w;vv>=u[i];vv--)    {        f[vv]=max(f[vv],f[vv-u[i]]+v[i]);    }    printf("%d",f[w]);    return 0;}

这里写图片描述

原创粉丝点击