11package class061 ;
22
3- // Prim算法模版 (洛谷)
3+ // Prim算法优化 (洛谷)
44// 静态空间实现
5+ // 时间复杂度O(n + m) + O((m+n) * log n)
56// 测试链接 : https://www.luogu.com.cn/problem/P3366
67// 请同学们务必参考如下代码中关于输入、输出的处理
78// 这是输入输出处理效率很高的写法
1516import java .io .StreamTokenizer ;
1617import java .util .Arrays ;
1718
18- // 以下的实现,即便是比赛也能通过
19- // 建图用链式前向星、 堆也是用数组结构手写的,不用任何动态结构
19+ // 建图用链式前向星
20+ // 堆也是用数组结构手写的、且只和节点个数有关
2021// 这个实现留给有需要的同学
2122// 但是一般情况下并不需要做到这个程度
23+
2224public class Code02_PrimStatic {
2325
2426 public static int MAXN = 5001 ;
@@ -38,26 +40,26 @@ public class Code02_PrimStatic {
3840
3941 public static int cnt ;
4042
41- // 因为边的权重数据都在weight数组中
42- // 所以需要比较边的权重,只需要有边的编号
43- // 就可以从weight数组中拿出数据比较了
44- // 所以小根堆只需要存储边的编号
45- public static int [] heap = new int [MAXM ];
43+ // 改写的堆结构
44+ public static int [][] heap = new int [MAXN ][2 ];
4645
47- public static int heapSize ;
46+ // where[v] = -1,表示v这个节点,从来没有进入过堆
47+ // where[v] = -2,表示v这个节点,已经弹出过了
48+ // where[v] = i(>=0),表示v这个节点,在堆上的i位置
49+ public static int [] where = new int [MAXN ];
4850
49- // 节点有没有进入集合
50- public static boolean [] set = new boolean [ MAXN ] ;
51+ // 堆的大小
52+ public static int heapSize ;
5153
52- // 一共有多少个点进入了集合
54+ // 找到的节点个数
5355 public static int nodeCnt ;
5456
5557 public static void build () {
5658 cnt = 1 ;
5759 heapSize = 0 ;
5860 nodeCnt = 0 ;
5961 Arrays .fill (head , 1 , n + 1 , 0 );
60- Arrays .fill (set , 1 , n + 1 , false );
62+ Arrays .fill (where , 1 , n + 1 , - 1 );
6163 }
6264
6365 public static void addEdge (int u , int v , int w ) {
@@ -67,41 +69,70 @@ public static void addEdge(int u, int v, int w) {
6769 head [u ] = cnt ++;
6870 }
6971
70- // 编号为e的边进入小根堆,根据边的权重来组织小根堆
71- public static void push (int e ) {
72- int i = heapSize ++;
73- heap [i ] = e ;
74- while (weight [heap [i ]] < weight [heap [(i - 1 ) / 2 ]]) {
72+ // 当前处理的是编号为ei的边!
73+ public static void addOrUpdateOrIgnore (int ei ) {
74+ int v = to [ei ];
75+ int w = weight [ei ];
76+ // 去往v点,权重w
77+ if (where [v ] == -1 ) {
78+ // v这个点,从来没有进入过堆!
79+ heap [heapSize ][0 ] = v ;
80+ heap [heapSize ][1 ] = w ;
81+ where [v ] = heapSize ++;
82+ heapInsert (where [v ]);
83+ } else if (where [v ] >= 0 ) {
84+ // v这个点的记录,在堆上的位置是where[v]
85+ heap [where [v ]][1 ] = Math .min (heap [where [v ]][1 ], w );
86+ heapInsert (where [v ]);
87+ }
88+ }
89+
90+ public static void heapInsert (int i ) {
91+ while (heap [i ][1 ] < heap [(i - 1 ) / 2 ][1 ]) {
7592 swap (i , (i - 1 ) / 2 );
7693 i = (i - 1 ) / 2 ;
7794 }
7895 }
7996
80- // 小根堆中弹出权重最小的边的编号
81- public static int pop () {
82- int e = heap [0 ];
83- heap [0 ] = heap [--heapSize ];
84- int i = 0 ;
97+ public static int u ;
98+
99+ public static int w ;
100+
101+ // 堆顶的记录:节点 -> u、到节点的花费 -> w
102+ public static void pop () {
103+ u = heap [0 ][0 ];
104+ w = heap [0 ][1 ];
105+ swap (0 , --heapSize );
106+ heapify (0 );
107+ where [u ] = -2 ;
108+ nodeCnt ++;
109+ }
110+
111+ public static void heapify (int i ) {
85112 int l = 1 ;
86113 while (l < heapSize ) {
87- int best = l + 1 < heapSize && weight [ heap [l + 1 ]] < weight [ heap [l ]] ? l + 1 : l ;
88- best = weight [ heap [best ]] < weight [ heap [i ]] ? best : i ;
114+ int best = l + 1 < heapSize && heap [l + 1 ][ 1 ] < heap [l ][ 1 ] ? l + 1 : l ;
115+ best = heap [best ][ 1 ] < heap [i ][ 1 ] ? best : i ;
89116 if (best == i ) {
90117 break ;
91118 }
92119 swap (best , i );
93120 i = best ;
94121 l = i * 2 + 1 ;
95122 }
96- return e ;
97123 }
98124
99125 public static boolean isEmpty () {
100126 return heapSize == 0 ;
101127 }
102128
129+ // 堆上,i位置的信息 和 j位置的信息 交换!
103130 public static void swap (int i , int j ) {
104- int tmp = heap [i ];
131+ int a = heap [i ][0 ];
132+ int b = heap [j ][0 ];
133+ where [a ] = j ;
134+ where [b ] = i ;
135+ int [] tmp = heap [i ];
105136 heap [i ] = heap [j ];
106137 heap [j ] = tmp ;
107138 }
@@ -116,9 +147,12 @@ public static void main(String[] args) throws IOException {
116147 m = (int ) in .nval ;
117148 build ();
118149 for (int i = 0 , u , v , w ; i < m ; i ++) {
119- in .nextToken (); u = (int ) in .nval ;
120- in .nextToken (); v = (int ) in .nval ;
121- in .nextToken (); w = (int ) in .nval ;
150+ in .nextToken ();
151+ u = (int ) in .nval ;
152+ in .nextToken ();
153+ v = (int ) in .nval ;
154+ in .nextToken ();
155+ w = (int ) in .nval ;
122156 addEdge (u , v , w );
123157 addEdge (v , u , w );
124158 }
@@ -131,23 +165,18 @@ public static void main(String[] args) throws IOException {
131165 }
132166
133167 public static int prim () {
134- set [ 1 ] = true ;
168+ // 1节点出发
135169 nodeCnt = 1 ;
170+ where [1 ] = -2 ;
136171 for (int ei = head [1 ]; ei > 0 ; ei = next [ei ]) {
137- push (ei );
172+ addOrUpdateOrIgnore (ei );
138173 }
139174 int ans = 0 ;
140175 while (!isEmpty ()) {
141- int edge = pop ();
142- int u = to [edge ];
143- int w = weight [edge ];
144- if (!set [u ]) {
145- set [u ] = true ;
146- nodeCnt ++;
147- ans += w ;
148- for (int ei = head [u ]; ei > 0 ; ei = next [ei ]) {
149- push (ei );
150- }
176+ pop ();
177+ ans += w ;
178+ for (int ei = head [u ]; ei > 0 ; ei = next [ei ]) {
179+ addOrUpdateOrIgnore (ei );
151180 }
152181 }
153182 return ans ;
0 commit comments