Thuật toán Prim giải tay toán rời rạc | Bài tập

Thuật toán Kruskal làm việc kém hiệu quả đối với những đồ thị có số cạnh khoảng m=n(n-1)/2. Trong những tình huống như vậy, thuật toán Prim giải tay tìm cây khung nhỏ nhất tỏ ra hiệu quả hơn. Bài viết dưới đây, TTnguyen sẽ cùng các bạn tìm hiểu lý thuyết, bài tập mô phỏng và cách cài đặt Prim.

Xem thêm:

I. Thuật toán Prim (Prim’s Algorithm)

Trong thuật toán này, bắt đầu tại một đỉnh tuỳ ý s của đồ thị, nối s với đỉnh y sao cho trọng số cạnh c[s, y] là nhỏ nhất. Tiếp theo, từ đỉnh s hoặc y tìm cạnh có độ dài nhỏ nhất, điều này dẫn đến đỉnh thứ ba z và ta thu được cây bộ phận gồm 3 đỉnh 2 cạnh. Quá trình được tiếp tục cho tới khi ta nhận được cây gồm n-1 cạnh, đó chính là cây bao trùm nhỏ nhất cần tìm.

Bài toán tìm cây khung nhỏ nhất của một đồ thị là một trong những bài toán tối ưu trên đồ thị có rất nhiều ứng dụng trong thực tế. Để minh họa cho thuật toán này, ta có thể xem xét một số mô hình thực tế điển hình:

  • Bài toán xây dựng đường giao thông: Giả sử chúng ta cần xây dựng một hệ thống đường bộ để kết nối n thành phố, đảm bảo rằng giữa bất kỳ hai thành phố nào cũng có một con đường. Nhiệm vụ đặt ra là tìm cây khung nhỏ nhất trên đồ thị, trong đó mỗi thành phố là một đỉnh và mục tiêu là tối thiểu hóa tổng chi phí xây dựng hệ thống đường giao thông này.
  • Bài toán nối mạng máy tính: Giả sử có n máy tính cần kết nối với nhau trong một hệ thống mạng. Biết rằng chi phí kết nối giữa máy i và máy j là c[i,j], với i,j = 1, 2, …, n (thông thường, chi phí này phụ thuộc vào độ dài cáp kết nối). Mục tiêu là tìm ra cách kết nối sao cho tổng chi phí nối mạng là thấp nhất.

Một số khái niệm:

  • Cây khung là một tập các cạnh sao cho tập cạnh này không tồn tại chu trình và liên thông
  • Cây khung nhỏ nhất là cây khung mà tổng trọng số các cạnh thuộc cây khung là nhỏ nhất

Khái niệm cây khung nhỏ nhất

Tải full bài tập thực hành lập trình C/C++ có lời giải:

II. Độ phức tạp của thuật toán Prim

Độ phức tạp thời gian của thuật toán Prim là O(E log V), trong đó E là số cạnh và V là số đỉnh trong đồ thị. Độ phức tạp không gian của thuật toán Prim là O(V + E).

III. Bài tập giải tay thuật toán Prim

Bài 1: Dùng thuật toán Prim tìm cây khung nhỏ nhất của đồ thị có ma trận trọng số sau:

thuật toán Prim tìm cây khung nhỏ nhất

Giải

thuật toán Prim tìm cây khung nhỏ nhất

Tập cạnh của cây khung nhỏ nhất cần tìm là T={(X1,X3), (X3,X2), (X2,X8), (X8,X4), (X8,X6), (X6,X7), (X1,X5)} trọng số nhỏ nhất bằng : 13+15+12+19+14+17+11 = 101. Cây khung được vẽ như sau:

thuật toán Prim tìm cây khung nhỏ nhất của đồ thị

Bài 2: Tìm cây bao trùm ngắn nhất prim:

Thuật toán Prim giải tay

Giải

Bước 1: \( X_1 = { 1}, V_1 = {∅} \)
Bước 2: \( X_2 = { 1,2}, V_2 = {(1,2)} \)
Bước 3: \( X_3 = { 1,2,3}, V_3 = {(1,2),(2,3)} \)
Bước 4: \( X_4 = { 1,2,3,4}, V_4 = {(1,2),(2,3),(3,4)} \)
Bước 5: \( X_5 = { 1,2,3,4,6}, V_5 = {(1,2),(2,3),(3,4),(3,6)} \)
Bước 6: \( X_6 = X_5 ∪ {7}, V_6 = V_5 ∪ {(6,7)} \)
Bước 7: \( X_7 = X_6 ∪ {5}, V_7 = V_6 ∪ {(6,5)} \)
Bước 8: \( X_8 = X_7 ∪ {8}, V_8 = V_7 ∪ {(5,8)} \)
Bước 9: \( X_9 = X_8 ∪ {11}, V_9 = V_8 ∪ {(8,11)} \)
Bước 10: \( X_{10} = X_9 ∪ {9}, V_{10} = V_9 ∪ {(8,9)} \)
Bước 11: còn lại đỉnh 10, đỉnh gần đỉnh 10 nhất là đỉnh \(9 X_{11} = X_{10} ∪ {10}, V_{11} = V_{10} ∪ {(9,10)} \)

Thuật toán Prim giải tay

IV. Cài đặt thuật toán Prim c++

1. Mô tả thuật toán Prim

Các bước chính của thuật toán Prim tìm cây phủ nhỏ nhất T của đồ thị liên thông có trọng số G được mô tả như sau:

Bước 1 : T := {v} v là đỉnh bất kỳ.

Bước 2 : Lặp n-1 lần

– Tìm đỉnh rìa v có cạnh e nối T với trọng số nhỏ nhất

– Đưa e vào T

2. Code thuật toán Prim

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <math.h>

#include <dos.h>

#define TRUE 1
#define FALSE 0
#define MAX 10000
int a[100][100];
int n, m, i, sc, w;
int chuaxet[100];
int cbt[100][3];
FILE * f;
void nhap(void) {
  int p, i, j, k;
  for (i = 1; i <= n; i++)
    for (j = 1; j <= n; j++)
      a[i][j] = 0;
  f = fopen("baotrum.in", "r");
  fscanf(f, "%d%d", & n, & m);
  printf("\n So dinh: %3d ", n);
  printf("\n So canh: %3d", m);
  printf("\n Danh sach canh:");
  for (p = 1; p <= m; p++) {
    fscanf(f, "%d%d%d", & i, & j, & k);
    printf("\n %3d%3d%3d", i, j, k);
    a[i][j] = k;
    a[j][i] = k;
  }
  for (i = 1; i <= n; i++) {
    printf("\n");
    for (j = 1; j <= n; j++) {
      if (i != j && a[i][j] == 0)
        a[i][j] = MAX;
      printf("%7d", a[i][j]);
    }
  }
  fclose(f);
  getch();
}
void Result(void) {
  for (i = 1; i <= sc; i++)
    printf("\n %3d%3d", cbt[i][1], cbt[i][2]);
}
void PRIM(void) {
  int i, j, k, top, min, l, t, u;
  int s[100];
  sc = 0;
  w = 0;
  u = 1;
  for (i = 1; i <= n; i++)
    chuaxet[i] = TRUE;
  top = 1;
  s[top] = u;
  chuaxet[u] = FALSE;
  while (sc < n - 1) {
    min = MAX;
    for (i = 1; i <= top; i++) {
      t = s[i];
      for (j = 1; j <= n; j++) {
        if (chuaxet[j] && min > a[t][j]) {
          min = a[t][j];
          k = t;
          l = j;
        }
      }
    }
    sc++;
    w = w + min;
    cbt[sc][1] = k;
    cbt[sc][2] = l;
    chuaxet[l] = FALSE;
    a[k][l] = MAX;
    a[l][k] = MAX;
    top++;
    s[top] = l;
    printf("\n");
  }
}
void main(void) {
  clrscr();
  nhap();
  PRIM();
  printf("\n Do dai ngan nhat:%d", w);
  for (i = 1; i <= sc; i++)
    printf("\n %3d%3d", cbt[i][1], cbt[i][2]);
  getch();
}

Trên đây là lý thuyết và bài tập vận dụng thuật toán prim toán rời rạc. Cảm ơn các bạn đã tham khảo trên ttnguyen.net

Bài viết liên quan:

Thuật toán cờ caro ma trận nxm c++

Thuật toán mã hóa Caesar c++

Nguyễn Tiến Trường

Mình viết về những điều nhỏ nhặt trong cuộc sống, Viết về câu chuyện những ngày không có em