普里姆(prim)算法练习
题目描述
又到了一年一度的明明生日了,明明想要买 B 样东西,巧的是,这 B 样东西价格都是 A 元。
但是,商店老板说最近有促销活动,也就是:
如果你买了第 I 样东西,再买第 J 样,那么就可以只花 KI,J 元,更巧的是,KI,J 竟然等于 KJ,I。
现在明明想知道,他最少要花多少钱。
输入格式
第一行两个整数,A,B。
接下来 B 行,每行 B 个数,第 I 行第 J 个为 KI,J。
我们保证 KI,J=KJ,I 并且 KI,I=0。
特别的,如果 KI,J=0,那么表示这两样东西之间不会导致优惠。
注意 KI,J 可能大于 A。
输出格式
一个整数,为最小要花的钱数。
输入输出样例
输入 #1
1 1 0
输出 #1
1
输入 #2
3 3 0 2 4 2 0 2 4 2 0
输出 #2
7
说明/提示
样例解释 2.
先买第 2 样东西,花费 3 元,接下来因为优惠,买 1,3 样都只要 2 元,共 7 元。
(同时满足多个“优惠”的时候,聪明的明明当然不会选择用 4 元买剩下那件,而选择用 2 元。)
数据规模
对于 30% 的数据,1≤�≤101≤B≤10。
对于 100% 的数据,1≤B≤500,0≤A,KI,J≤1000。
2018.7.25新添数据一组
解题思路
采用邻接矩阵储存图,对于注意 KI,J 可能大于 A。我们在邻接矩阵的输入时可以取m毕竟打折更贵那我还不如不要打折,如果两者没有联系也可以取m代表两者联系的打折价格后就是m,这样处理就只用构造一棵最小生成树了
AC代码
#include
int a[501][501], dis[501], book[501];//book标记这个物品是否买了
int main()
{
int m, n, i, j;
scanf("%d %d", &m, &n);
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
{
scanf("%d", &a[i][j]);
if ((a[i][j] > m) || (i != j && a[i][j] == 0))//如果打折后更贵或两个物品之间没有折扣
a[i][j] = m;
}
for (i = 1; i <= n; i++)//取1点为起始点
dis[i] = a[1][i];
book[1] = 1;
int sum = m;//初始购买物品1。
for (i = 2; i <= n; i++)
{
int min = 1e9, k = 1;
for (j = 2; j <= n; j++)
if (book[j] == 0 && min > dis[j])//先找到价格最便宜的
{
k = j; min = dis[j];
}
if (k == 1)//k==1代表所有物品买了
break;
sum += min; book[k] = 1;//统计和并标记
for (j = 2; j <= n; j++)//松弛
if (book[j] == 0 && dis[j] > a[k][j])
dis[j] = a[k][j];
}
printf("%d", sum);
return 0;
}
克鲁斯卡尔(Kruskal)算法练习
题目背景
“咚咚咚……”“查水表!”原来是查水表来了,现在哪里找这么热心上门的查表员啊!小明感动得热泪盈眶,开起了门……
题目描述
妈妈下班回家,街坊邻居说小明被一群陌生人强行押上了警车!妈妈丰富的经验告诉她小明被带到了 t 区,而自己在 s 区。
该市有 m 条大道连接 n 个区,一条大道将两个区相连接,每个大道有一个拥挤度。小明的妈妈虽然很着急,但是不愿意拥挤的人潮冲乱了她优雅的步伐。所以请你帮她规划一条从 s 至 t 的路线,使得经过道路的拥挤度最大值最小。
输入格式
第一行有四个用空格隔开的 n,m,s,t,其含义见【题目描述】。
接下来 m 行,每行三个整数 u,v,w,表示有一条大道连接区 u 和区 v,且拥挤度为 w。
两个区之间可能存在多条大道。
输出格式
输出一行一个整数,代表最大的拥挤度。
输入输出样例
输入 #1
3 3 1 3 1 2 2 2 3 1 1 3 3
输出 #1
2
说明/提示
数据规模与约定
样例输入输出 1 解释
小明的妈妈要从 1 号点去 3 号点,最优路线为 1->2->3。
解题思路
本题采用克鲁斯卡尔(Kruskal)算法时间消耗相对较少,在建造最小生成树的过程中并不需要建好在找s到t的最小生成树,只需要在建造过程中建造完一条边后s能够到t,那么刚刚建造的那条边就是最小拥挤度,因为刚开始的时候根据拥挤度进行排序了的。
#include
struct node {//l点到r点的拥挤度为data
int l, r, data;
}a[20001];
int n, m, s, t;
void kp(int x, int y)//快速排序不多说
{
if (x >= y)return;
int left = x, right = y;
struct node temp = a[x], t;
while (left < right)
{
while (a[right].data >= temp.data && left < right)
right--;
while (a[left].data <= temp.data && left < right)
left++;
if (left < right)
{
t = a[left]; a[left] = a[right]; a[right] = t;
}
}
a[x] = a[left]; a[left] = temp;
kp(x, left - 1);
kp(left + 1, y);
}
int b[10001];//并查集数组
int find(int x)
{
if (b[x] == 0)
return x;
else
{
b[x] = find(b[x]);//路径压缩
return b[x];
}
}
int main()
{
scanf("%d %d %d %d", &n, &m, &s, &t);
for (int i = 1; i <= m; i++)
scanf("%d %d %d", &a[i].l, &a[i].r, &a[i].data);
kp(1, m);//升序排序
for (int i = 1; i <= m; i++)
{
int X = find(a[i].l), Y = find(a[i].r);
if (X != Y)
b[X] = Y;
if (find(s) == find(t))//有s与t有联系
{
printf("%d", a[i].data);//输出这个拥挤度
return 0;//直接结束
}
}
return 0;
}
题目背景
小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。
有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。
题目描述
给你云朵的个数 N,再给你 M 个关系,表示哪些云朵可以连在一起。
现在小杉要把所有云朵连成 K 个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。
输入格式
第一行有三个数 N,M,K。
接下来 M 行每行三个数 X,Y,L,表示 X 云和 Y 云可以通过 L 的代价连在一起。
输出格式
对每组数据输出一行,仅有一个整数,表示最小的代价。
如果怎么连都连不出 K 个棉花糖,请输出 No Answer
。
输入输出样例
输入 #1
3 1 2 1 2 1
输出 #1
1
说明/提示
对于 30%的数据,1≤N≤100,1≤M≤10^3;
对于 100% 的数据,1≤N≤10^3,1≤M≤10^4,1≤K≤10,1≤X,Y≤N,0≤L<10^4。
解题思路
坑点:建立结点数量n的最小生成树需要n-1,n个结点中建立k个最小生成树需要n-k条边,n个结点的图中两种情况无法建成k个最小生成树 1.m 其余克鲁斯卡尔算法操作,一个变量统计最小代价。 AC代码#include