搭配购买tj

Joe觉得云朵很美,决定去山上的商店买一些云朵。商店里有n多云,云朵被编号为1,2,.....n,并且每朵云都有一个价值。
但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。
但是Joe的钱有限,所以他希望买的价值越多越好。


题目概述

有n个点,m条线将它们中的一部分连接,每个点有它的价值和权值。
获得这个点要付出它的权值并获得与它连接的点,求在付出不超过w时,可以获得的最大价值。

输入

第1行:n、m、w,表示n多云,m个搭配,Joe有w的钱

第2至n+1行,每行ci,di表示i朵云的价钱和价值。

第n+2至n+1+m行,每行ui,vi表示买ui就必须买vi,同理,如果买vi就必须买ui。

输出

一行:表示可以获得的最大价值

思路

如下图,有n个点,m条边,并且是双向的,很容易联想到图,
将问题转化为求每个连通块的价值和付出的权值,
再套上01背包模板求最大价值。

代码实现

可以用并查集来储存,然后缩点:

for(int i=1; i<=n; i++)
{
int v=find(i);
if(!vis[v])
{
cnt++;
vis[v]=cnt;
}
c[vis[v]]+=e[i].x;
w[vis[v]]+=e[i].y;
}

但这里我用递推来寻找连通块:

void find(int u)
{
for(auto it=v[u].begin();it!=v[u].end();it++)//v[i]用于储存以i为定点的连通块所包含的点
{
if(vis[*it]==true)//如果这个点之前已经寻找过,那就跳过,避免重复计算
{
continue;
}
vis[*it]=true;
nc+=a[*it].c;//nc是这个连通块需付出的权值总和
nd+=a[*it].d;//nd是连通块可以获得的价值总和
find(*it);//递归查找
}
}

Ac Code

#include <bits/stdc++.h>
using namespace std;
//分割线qwq
int n,m,w;
int nc=0,nd=0;
int cnt;
bool vis[10005];
vector<int> v[10005];
int dp[100005];
//分割线qwq
struct yun
{
int c;
int d;
}a[10005];
struct cd
{
int c;
int d;
}cdd[10005];
//分割线qwq
void find(int u)
{
for(auto it=v[u].begin();it!=v[u].end();it++)
{
if(vis[*it]==true)
{
continue;
}
vis[*it]=true;
nc+=a[*it].c;
nd+=a[*it].d;
find(*it);
}
}
//分割线qwq
int main()
{
cin>>n>>m>>w;
for(int i=1;i<=n;i++)
{
int c,d;
cin>>c>>d;
a[i].c=c;
a[i].d=d;
}
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
v[x].push_back(y);
v[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
if(vis[i]==false)
{
nc=nd=0;
vis[i]=true;
nc+=a[i].c;
nd+=a[i].d;
find(i);
if(nc<=w)
{
cnt++;
cdd[cnt].c=nc;
cdd[cnt].d=nd;
}
}
}
for(int i=1;i<=cnt;i++)
{
for(int j=w;j>=cdd[i].c;j--)
{
dp[j]=max(dp[j],dp[j-cdd[i].c]+cdd[i].d);
}
}
cout<<dp[w];//01背包,作者不会不会吧qwq
return 0;
}

并查集代码(感谢dcz提供)

#include <bits/stdc++.h>
using namespace std;
int n,m,dp[200001],pre[200001],k,c[200001],w[200001],cnt,ans,vis[100001];
struct node
{
int x,y;
} e[200001];
int find(int x)
{
return x==pre[x]?x:pre[x]=find(pre[x]);
}
void merge(int x,int y)
{
int rx=find(x),ry=find(y);
if(rx!=ry)
{
pre[ry]=rx;
}
}
int main()
{
cin>>n>>m>>k;
for(int i=1; i<=n; i++)
{
cin>>e[i].x>>e[i].y;
pre[i]=i;
}
for(int i=1; i<=m; i++)
{
int x,y;
cin>>x>>y;
merge(x,y);
}
for(int i=1; i<=n; i++)
{
int v=find(i);
if(!vis[v])
{
cnt++;
vis[v]=cnt;
}
c[vis[v]]+=e[i].x;
w[vis[v]]+=e[i].y;
}
for(int i=1; i<=cnt; i++)
{
for(int j=k; j>=c[i]; j--)
{
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
ans=max(ans,dp[j]);
}
}
cout<<ans;
return 0;
}

总结一下

这是一道大水题,考点不明,只要注意细节就可以AC
正解仿佛是Tarjan,作者懒,改天补上

posted @   Funny_Cat  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
一键三连
点击右上角即可分享
微信分享提示