题解思路:用到Kruskal最小生成树求法,题解在代码部分,废话不多说,上代码及题解:
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+10,M=510;
int a,b;//a代表初始每个礼物的价钱,b代表礼物个数
int cnt;//记录边的个数
int p[M];//并查集数组p,用于判断是否为同一集合
//结构体用于存边
struct st{
int a,b,w;
}ed[N];
//重定义结构体排序,按边权值排序
bool cmp(st a,st b)
{
return a.w<b.w;
}
//并查集find函数,
int find(int x)
{
//路径压缩,让每个点直接指向根节点
if(p[x]!=x) p[x]=find(p[x]);
return p[x];//返回根节点
}
int main()
{
cin>>a>>b;
//初始化p数组,初始,每个元素都是一个集合,所以自己指向自己 ,记得从0到b [0,b],因为有0号节点
for(int i=0;i<=b;i++) p[i]=i;
//将0号节点与其他所有节点建一条权值为a的边,也就对应,可以直接按原价格a元购买某一礼物
for(int i=1;i<=b;i++)
{
ed[cnt]={0,i,a};
cnt++;
}
int x;
for(int i=1;i<=b;i++)
{
for(int j=1;j<=b;j++)
{
cin>>x;
//x==0时,代表没有优惠或是i==j没有意义,
//i<j,是确保没有重边
if(x!=0 && i<j)
{
ed[cnt]={i,j,x};
cnt++;
}
}
}
//排序
sort(ed,ed+cnt,cmp);
//res记录最小生成树的值
int res=0;
for(int i=0;i<cnt;i++)//总共cnt条边
{
int a=ed[i].a,b=ed[i].b,w=ed[i].w;
//如果find(a)!=find(b) ,表示a和b的根节点不一致,也就不在同一集合,就累加边权值
//并将a和b集合合并,操作就是让a的根节点指向b的根节点
if(find(a)!=find(b))
{
res+=w;
p[find(a)]=find(b);
}
}
//输出答案
cout<<res;
return 0;
}