搭配购买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,作者懒,改天补上
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现