P1194 买礼物

Portal.

最小生成树。

一个貌似比较常见的图论建模。对于每个物品可以抽象成一个点,建立超级源点。把每个物品与超级源点连一条边权为 A A A 的边表示直接购买这个物品,在把有优惠关系的一对物品连一条边权为优惠价格 K K K 的边。

这样转化之后,买到所有的物品就等价于使整张图连通。要求最小代价,求最小生成树即可。

注意区分 A A A B B B

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=3e5+5;
struct edge{int u,v,w;}e[maxn];
int fa[maxn];

bool cmp(edge a,edge b){return a.w<b.w;}

int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}

int main()
{
	int A,B;cin>>A>>B;
	for(int i=1;i<=B;i++) e[i]=(edge){0,i,A},fa[i]=i;
	int cnt=B;
	for(int i=1;i<=B;i++)
		for(int j=1;j<=B;j++)
		{
			int K;cin>>K;
			if(K) e[++cnt]={i,j,K};
		}
	sort(e+1,e+cnt+1,cmp);
	int tot=0;
	ll ans=0;
	for(int i=1;i<=cnt;i++)
	{
		int fx=find(e[i].u),fy=find(e[i].v);
		if(fx==fy) continue;
		tot++,fa[fx]=fy,ans+=e[i].w;
		if(tot==B) break;
	}
	cout<<ans;
	return 0;
}
<think>好的,用户问的是洛谷的P1194题目,我需要先确认这道题的具体内容。洛谷是一个在线编程评测平台,题目编号P1194对应的题目可能涉及某种算法或数据结构。首先,我应该回忆一下常见的题目类型,比如动态规划、图论、贪心算法等。 不过,用户可能直接需要题目的解析或者解题思路,而不仅仅是知道题目存在。我需要先确定P1194的具体题目名称和内容。假设我记得P1194是关于礼物的题目,可能涉及到图论中的最小生成树应用,比如Kruskal算法或者Prim算法。比如题目描述可能是购礼物需要连接各个优惠条件,求最小花费。 接下来要检查题目是否确实如此,可能需要快速查找或回忆。例如,洛谷P1194的题目通常标题是“礼物”,题意大致是:有n个物品,每个物品原价a元,但当购过某个物品后,其他物品可能有优惠价b元。需要找到购所有物品的最小总花费。这种情况下,可以构建一个图,将每个物品看作节点,优惠条件看作边,然后求最小生成树加上第一个物品的原价。 需要确认是否正确,避免误导用户。比如,正确的解法应该是将第一个物品以原价购,然后其他物品通过优惠价连接,构造最小生成树,总费用为a + (n-1)*最小生成树的边权和。或者可能所有物品都需要考虑是否使用原价或优惠价,这时候可能需要建立一个虚拟节点,将所有物品与原价连接,再应用最小生成树算法。 然后,我需要将这些思路整理成步骤,包括如何建模、选择算法、处理特殊情况(如优惠价比原价还高的情况),并给出代码的大致框架,比如使用Kruskal算法并处理并查集的结构。 同时,需要注意用户可能的水平,如果是刚接触图论的新手,可能需要解释最小生成树的概念,以及为什么这道题适用该算法。如果用户需要代码示例,应提供伪代码或具体实现的注意事项,比如边的排序方式、如何判断是否形成环等。 最后,提醒用户注意常见错误,例如没有正确初始化并查集,或者没有将虚拟节点正确连接到各个物品,导致生成树不包含所有节点。此外,测试样例的验证也很重要,比如当所有优惠价都比原价高时,直接每个物品都按原价购会更优,这时候需要确保算法能正确处理这种情况。 总结起来,回答需要包括题目分析、解题思路、算法选择、代码实现要点以及注意事项,确保用户能够理解并应用解决方案。</think>喵~ 洛谷P1194是一道经典的**最小生成树变形题**哦!题目全称是《礼物》,主要考察对Kruskal/Prim算法的灵活运用~(=^・ω・^=) **题目核心模型**: 把每个礼物看作节点,建立虚拟节点"0"代表初始购状态。若购物品$i$后能用优惠价$w$$j$,则建立边$i \xrightarrow{w} j$,同时所有物品都有直购价$a$对应的边$0 \xrightarrow{a} i$。求包含所有节点的最小生成树。 **算法步骤**: 1. 构建带权边集合 $$\forall i \in [1,n],\ add\ edge(0,i,a)$$ $$\forall (i,j,w) \in 优惠表,\ add\ edge(i,j,w)$$ 2. 按边权升序排序 3. 用Kruskal算法选边,累计总花费 **代码框架**(Python): ```python def find(p, x): while p[x] != x: p[x] = p[p[x]] x = p[x] return x n, m = map(int, input().split()) edges = [] for i in range(1, m+1): for j, w in enumerate(map(int, input().split())): if w != 0 and i < j+1: edges.append((w, i, j+1)) edges.append((n, 0, i)) # 原价边 edges.sort() parent = list(range(m+1)) res = 0 for w, u, v in edges: pu = find(parent, u) pv = find(parent, v) if pu != pv: parent[pu] = pv res += w print(res) ``` **注意事项**: 1. **虚拟节点**必须包含所有物品的直购价 2. **边权比较**:当优惠价≥原价时,直接购更优 3. **特判**当所有优惠无效时,总价为$n \times a$ 需要我演示具体测试案例吗?(ฅ´ω`ฅ) 比如当输入: ``` 3 3 0 2 4 2 0 2 4 2 0 ``` 最小花费应该是$3 + 2 = 5$元哦~
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值