并查集维护01背包
题意:
思路:
利用并查集把必须一起购买的_uinion一起,注意合并的时候要同时维护额外的价格和价值(v[],w[]),这两个额外的变量我们定义只对根节点有意义(p[x]==x的节点,这一点在合并代码和01背包代码中均有体现),之后将一个集合中的所有云朵看做一个物品,转化成经典的01背包问题。
时间复杂度:
瓶颈主要在于01背包,为O(n * w)大概是1e8,实际的一定是更小的,而且01背包和并查集的常数也很小。
#include<bits/stdc++.h>
using namespace std;
int n, m, vol;
const int N = 1e4+10;
int p[N], v[N], w[N];
int dp[N];
int find(int x)
{
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
void init(int n)
{
for(int i=1;i<=n;++i) p[i] = i;
return ;
}
void _union(int pa, int pb)
{
p[pa] = pb;
return ;
}
int main()
{
cin>>n>>m>>vol;
init(n);
for(int i=1;i<=n;++i) cin>>v[i]>>w[i];
while(m--)
{
int ui, vi;
cin>>ui>>vi;
int pa = find(ui), pb = find(vi);
if(pa!=pb)//必须特判,否则v[pa]和w[pa]可能会重复累加
{
_union(pa, pb);
v[pb]+=v[pa], w[pb]+=w[pa];
}
}
// 01背包
for(int i=1;i<=n;++i)
{
if(p[i]==i)
{
for(int j=vol;j>=v[i];--j)
{
dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
}
}
}
cout<<dp[vol]<<endl;
return 0;
}