一个有条件的背包题,大意就是给出一个01背包题的数据,然后加上一些必须在一起的组合
题目:
一开始思路是写一个并查集,然后遍历把原来的背包数据更新后存在两个新的数组中(后来想想其实多此一举)(想想为什么不用把更新后的数据单放在其他数组里)
我的复杂ac代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10005;
int w[N],v[N],p[N];//原始数据和标记
int book[N];//存下标
int ww[N],vv[N];//新数据
int dp[N];//背包
int find(int x){
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main(){
int n,m,c;
cin>>n>>m>>c;
for(int i=1;i<=n;i++){
p[i]=i;
}//初始化
for(int i=1;i<=n;i++){
cin>>v[i]>>w[i];
}
int x,y;
for(int i=1;i<=m;i++){
cin>>x>>y;
p[find(x)]=find(y);//这句别忘
}//并查集核心操作
int vis=0;//下标
for(int i=1;i<=n;i++){
if(!book[find(p[i])])book[find(p[i])]=++vis;//存下标
ww[book[find(p[i])]]+=w[i];
vv[book[find(p[i])]]+=v[i];
}
int ans=0;
for(int i=1;i<=vis;i++){
for(int j=c;j>=vv[i];j--){
dp[j]=max(dp[j],dp[j-vv[i]]+ww[i]);//背包
}
}
cout<<dp[c]<<endl;
}
题解简洁代码:
#include <bits/stdc++.h>
using namespace std;
const int N=10010;
int p[N],w[N],v[N],f[N];
int n,m,c;
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main(){
cin >> n >> m >> c;
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i];
while(m--){
int a,b;
cin >> a >> b;
int pa=find(a);int pb=find(b);
if(pa!=pb){
w[pb]+=w[pa];
v[pb]+=v[pa];
p[pa]=p[pb];
}
}
for (int i = 1; i <= n; i ++ )
if (p[i] == i)//这里一开始没想到
for (int j = c; j >= v[i]; j -- )
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[c];
}
通过这个题解回答前面的伏笔
for (int i = 1; i <= n; i ++ )
if (p[i] == i)//这里一开始没想到
for (int j = c; j >= v[i]; j -- )
f[j] = max(f[j], f[j - v[i]] + w[i]);
这个思路就是在原先的数组里更改,利用了p[i]==i时是一个根节点的性质,能省不少行代码。