P1194 买礼物

买礼物

题目描述

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

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

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

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

输入格式

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

接下来 B B B 行,每行 B B B 个数,第 I I I 行第 J J J 个为 K I , J K_{I,J} KI,J

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

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

输出格式

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

样例 #1

样例输入 #1

1 1
0

样例输出 #1

1

样例 #2

样例输入 #2

3 3
0 2 4
2 0 2
4 2 0

样例输出 #2

7

提示

样例解释 2 2 2

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

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

数据规模

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

对于 100 % 100\% 100% 的数据, 1 ≤ B ≤ 500 , 0 ≤ A , K I , J ≤ 1000 1\le B\le500,0\le A,K_{I,J}\le1000 1B500,0A,KI,J1000

2018.7.25新添数据一组

#include
using namespace std;
#define FOR(i,n,s) for(int i=(s);i<=(n);i++)
const int MAX=1e6+5;
int a,b;
int cntedge=1,cntpoint=1,sum=0,tot=0;//sum记录边权和,cntedge记录连了多少条边,cntpoint记录遍历到那个结点,tot记录总边数 
struct node
{
	int u,v,w;
}edges[MAX];//存边的结构体


//
int f[MAX];//并查集的维护数组,表示根结点
int find(int x)
{
	if(x==f[x]) return x;
	else return f[x]=find(f[x]);
}//并查集查找函数:查找x的根节点
void init()
{
	FOR(i,b,1) f[i]=i;
}//并查集初始化函数:将每个结点的根结点先默认为它自己
void merge(int u,int v)
{
	f[find(u)]=find(v);
}//并查集合并函数:将u和v的根结点合并
//
//并查集


//
void addedge(int u,int v,int w)
{
	tot++;
	edges[tot].u=u;
	edges[tot].v=v;
	edges[tot].w=w;
}//加边 
bool cmp(node a,node b)
{
	return a.w<b.w;
}//按照边权从小到大排序
void kruskal()
{
	sort(edges+1,edges+1+tot,cmp);//Kruskal最小生成树的思想,将边权按照从小到大排序
	while(cntedge<=tot&&cntpoint<=b)
	{
		if(find(edges[cntedge].u)!=find(edges[cntedge].v))//如果个结点之间没有回路,则可以合并
		{
			merge(edges[cntedge].u,edges[cntedge].v);//合并
			sum+=edges[cntedge].w;//更新最小边权和
			cntpoint++;
		}
		cntedge++;
	}
}
//
//最小生成树


int main()
{
	cin>>a>>b;
	init();
	for (int i=1;i<=b;i++)
		for (int j=1;j<=b;j++)
		{
			int x;
			cin>>x;
			if(i>j || x==0) continue;
			addedge(i,j,x);
		}
	for(int i=1;i<=b;i++)
		addedge(0,i,a);//建立一个0结点(虚拟结点),表示所有的物品都能买到 
	kruskal();
	cout<<sum;
	return 0;
}

ps:

与上一个题类似,单源最短路,用kruskal解决,大体模版于上个题类似,但是需要建立一个0结点,表示所有的物品都能买得到

end

你可能感兴趣的:(算法)