题目链接:https://ac.nowcoder.com/acm/problem/14348

分析:读完题目我们可以发现,题目中频繁的涉及一个人和另一个人之间的关系,并且关系之间具有传递性,我们把所有认识的人看成一个集合,不难想到用并查集来维护所有相互认识的人构成的集合

  int find(int x){
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
  //别忘了初始化
  for(int i=1;i<=n;i++)
        p[i]=i;
  while(m--){
        int x,y;
        scanf("%d%d",&x,&y);
        int pa=find(x);
        int pb=find(y);
        if(pa!=pb) p[pa]=pb;
    }

接着分析,可以发现小d有一定的精力值c,并且和第i个人认识要花费ai的精力值,获得bi的利益值,求最大的利益值,很明显这是一个01背包问题,在小d认识的集合中做01背包,最后f[c]就是答案

  01背包
  memset(f,0,sizeof f);
  //因为有多组测试数据,所以不要忘了每次初始化dp数组
    for(int i=2;i<=n;i++)
        if(find(1)==find(i)){
            for(int j=c;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[i]]+g[i]);
        }
    printf("%d\n",f[c]);

Acode:

 #include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
const int N=1e5+5;
int t;
int n,m,c;
int p[N];
int w[N],g[N];
int f[N];
int find(int x){
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
void solve(){
    scanf("%d%d%d",&n,&m,&c);
    for(int i=1;i<=n;i++)
        p[i]=i;
    for(int i=2;i<=n;i++)
        scanf("%d%d",&w[i],&g[i]);
    while(m--){
        int x,y;
        scanf("%d%d",&x,&y);
        int pa=find(x);
        int pb=find(y);
        if(pa!=pb) p[pa]=pb;
    }
    memset(f,0,sizeof f);
    for(int i=2;i<=n;i++)
        if(find(1)==find(i)){
            for(int j=c;j>=w[i];j--)
                f[j]=max(f[j],f[j-w[i]]+g[i]);
        }
    printf("%d\n",f[c]);
}
signed main(){
    scanf("%d",&t);
    while(t--) solve();
    return 0;
}