[AcWing 1252]搭配购买 题解
搭配购买 题解
题目大意
用有限的钱去买云,相搭配的云一起购买,使总价值最大
题意分析
思路
既然相搭配的云朵必须一起购买,那么我们可以让所有搭配的云朵的价钱和价值统一为所有云朵的总和,最后要求的是最大价值,很显然是01背包的板子。
预处理
记录相搭配的云朵可以采用建图的方法,把输入的\(u_i,v_i\)相连接。
然后合并所有相连的边,把每条边dfs遍历一下,价钱与价值累加。
01背包求解
最后使用01背包求得最大价值,注意每个组合只能使用一次,所以在每选择一个新的组合后,把这个组合的所有节点标记为已使用。
\(Code\)(丑陋的赛时代码)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct edge {
int to,nex;
} e[N];
int n,m,w,tot;
int head[N],c[N],d[N],dp[N],f[N],a[N],t[N],s[N],l[N],vis[N];
void add(int x,int y) {
e[++tot].to=y;
e[tot].nex=head[x];
head[x]=tot;
}
void dfs(int x,int fa) {//遍历与x相连的边,使与x相搭配的云朵的价钱和价值都为它们的和
// cout<<x<<' ';
for(int i=head[x]; i; i=e[i].nex) {
int y=e[i].to;
if(y==fa) continue;
dfs(y,x);
// cout<<"|child: "<<y<<" cost:"<<a[y]<<"| ";
// cout<<a[y]<<"+";
a[x]+=a[y];
l[x]+=l[y];
}
}
void refresh(int x,int fa) {//用于更新云朵的状态,避免同一个组合重复选择
for(int i=head[x]; i; i=e[i].nex) {
int y=e[i].to;
vis[y]=1;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
memset(head,-1,sizeof(head));
cin>>n>>m>>w;
for(int i=1; i<=n; i++) {
cin>>c[i]>>d[i];
}
memcpy(t,c,sizeof(t));//保存原始信息
memcpy(s,d,sizeof(s));
for(int i=1,x,y; i<=m; i++) {//建图
cin>>x>>y;
add(x,y);
add(y,x);
}
// for(int i=1; i<=n; i++) {
// cout<<"root: "<<i<<" child: ";
// for(int j=head[i]; j; j=e[j].nex) {
// cout<<e[j].to<<" ";
// }
// cout<<"\n";
// }
for(int i=1; i<=n; i++) {//统一数据
// cout<<"c[i]= ";
memcpy(a,t,sizeof(a));
memcpy(l,s,sizeof(l));
dfs(i,i);
c[i]=a[i];
d[i]=l[i];
// cout<<"\n";
}
// for(int i=1; i<=n; i++) {
// cout<<c[i]<<"\n";
// }
for(int i=1; i<=n; i++) {//01背包
for(int j=w; j>=c[i]; j--) {
if(!vis[i]) {
dp[j]=max(dp[j],dp[j-c[i]]+d[i]);
refresh(i,i);
}
}
}
int maxn;
for(int i=1; i<=w; i++) {
maxn=max(maxn,dp[i]);
}
cout<<maxn;
return 0;
}