【一只蒟蒻的刷题历程】 洛谷 P1194 买礼物(prim最小生成树)

题目描述

又到了一年一度的明明生日了,明明想要买BBB样东西,巧的是,这BBB样东西价格都是AAA元。

但是,商店老板说最近有促销活动,也就是:

如果你买了第III样东西,再买第JJJ样,那么就可以只花KI,JK_{I,J}KI,J​元,更巧的是,KI,JK_{I,J}KI,J​竟然等于KJ,IK_{J,I}KJ,I​。

现在明明想知道,他最少要花多少钱。

输入格式

第一行两个整数,A,BA,BA,B。

接下来BBB行,每行BBB个数,第III行第JJJ个为KI,JK_{I,J}KI,J​。

我们保证KI,J=KJ,IK_{I,J}=K_{J,I}KI,J​=KJ,I​并且KI,I=0K_{I,I}=0KI,I​=0。

特别的,如果KI,J=0K_{I,J}=0KI,J​=0,那么表示这两样东西之间不会导致优惠。

输出格式

一个整数,为最小要花的钱数。

输入输出样例

输入 #1

1 1
0

输出 #1

1

输入 #2

3 3
0 2 4
2 0 2
4 2 0

输出 #2

7

说明/提示

样例解释2

样例解释222

先买第222样东西,花费333元,接下来因为优惠,买1,31,31,3样都只要222元,共777元。

(同时满足多个“优惠”的时候,聪明的明明当然不会选择用444元买剩下那件,而选择用222元。)

数据规模

对于30%30%30%的数据,1≤B≤101 \le B \le 101≤B≤10。

对于100%100%100%的数据,1≤B≤500,0≤A,KI,J≤10001 \le B \le 500,0 \le A,K_{I,J} \le 10001≤B≤500,0≤A,KI,J​≤1000。


思路:

最小生成树prim + 优先队列版的堆优化

这道题目的意思是有B个商品,每个商品的价格是A,但是商品会打折,就是你买了B中某个商品后,再买其他的商品可能会得到一个优惠,要注意的是,第一次买的商品保证没有优惠,然后其他的商品为0代表没优惠,有“优惠”时还可能比原价还贵(黑心商家 ),要注意判断。

最初没注意到最小生成树只会把后n-1条边的值加上,一直漏了第一个买的值(第一个值没有优惠,所以应该就是A的值),所以初始值看作A,然后加一遍prim 的路径上的值就可以了


代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std; 
struct node{
	int v,w;
	node(int a,int b){v=a;w=b;} //构造结构体
	friend bool operator<(node a,node b){ //重载
		return a.w>b.w;
	}
};

const int maxn=550;  
const int inf=1<<27;  //代表两点无法到达
int A,B,ans=0;    //每样价格A,总的B件物品,ans为答案
int x,dis[maxn];  //dis记录各点到已形成树(把已形成的看作整体)的最小路径
vector<node> g[maxn];  //邻接表存边
bool vis[maxn]={false}; //判断是否走过
priority_queue<node> q;  //优先队列用来模拟堆优化

int prim()
{
	fill(dis,dis+maxn,inf);
	dis[1]=A;   //注意,这里第一个初始设为A,因为没有优惠
	q.push(node(1,0)); //入队
	while(!q.empty())
	{
		int u=q.top().v; 
		q.pop();
		if(vis[u]) continue;
		ans += dis[u];  //加上该边
		vis[u]=1;  //记录走过
		for(int i=0;i<g[u].size();i++)
		{
			int v=g[u][i].v;  //取出下一个点
			int w=g[u][i].w;  //取出这条边的长度(折扣价格)
			if(vis[v]==false)  //没访问过才去访问
			{
				if(dis[v] > w) //优化距离
				dis[v]=w;
				q.push(node(v,dis[v])); //入队
			}
		}
	}
	return ans;
}
int main()
{
    cin>>A>>B;
    for(int i=1;i<=B;i++)
    {
    	for(int j=1;j<=B;j++)
    	{
    	   cin>>x; 	 //买了i后j的价格
    	   if(x==0) x=A;  //等于0代表没有优惠
    	   else x=min(x,A); //可能给的优惠价格比原价格还贵 
    	   g[i].push_back(node(j,x)); //双向边
    	   g[j].push_back(node(i,x));
		}
	}
	cout<<prim(); //输出结果
	return 0;
} 

你可能感兴趣的:(#,最小生成树)