You are on page 1of 62

1.

MA TRẬN ĐỈNH CUNG (ADD+EDGE) }


Cho cấu trúc dữ liệu đồ thị được khai báo sử dụng /* Tra ve so phan tu cua danh sach */
ma trận đỉnh - cung như sau: int count_list(List* L) {
return L->size;
typedef struct { }
int A[100][500];
int n, m; List neighbors(Graph* G, int x) {
} Graph; List L;
Giả sử đồ thị vô hướng, không chứa khuyên. make_null(&L);
Viết hàm add_edge(Graph* G, int e, int x, int y) để int e, y;
thêm cung e = (x, y) vào đồ thị G. for (y = 1; y <= G->n; y++) {
if (x == y) continue;
void add_edge(Graph* G, int e, int x, int y) {
for (e = 1; e <= G->m; e++)
}
if (G->A[x][e] > 0 && G-
>A[y][e] > 0) {
void add_edge(Graph*G, int e, int x, int y){
push_back(&L, y);
G->A[x][e] = 1;
break;
G->A[y][e] = 1;
}
}
}
return L;
2. MA TRẬN ĐỈNH CUNG (NEIGHBORS)
}
Cho cấu trúc dữ liệu đồ thị được khai báo sử dụng
ma trận đỉnh - cung như sau:
3. MA TRẬN ĐỈNH ĐỈNH (DEGREE)
typedef struct { Cho cấu trúc dữ liệu đồ thị được khai báo sử dụng
int A[100][500]; ma trận đỉnh - đỉnh như sau:
int n, m;
typedef struct {
} Graph; int A[100][100];
Giả sử đồ thị không chứa khuyên. int n;
} Graph;
Viết hàm List neighbors(Graph* G, int x) trả về danh
Giả sử đồ thị vô hướng, không chứa khuyên, không
sách các đỉnh kề của x.
chứa đa cung
Chú ý: các đỉnh kề của x được sắp xếp theo thứ tự
Viết hàm int deg(Graph* G, int x) để tính bậc của
tăng dần và không trùng nhau. Ví dụ: nếu các đỉnh
đỉnh x.
kề của 1 là 4 và 2 thì danh sách trả về chứa: 2 và 4.
int deg(Graph* G, int x) {
Cấu trúc dữ liệu List được định nghĩa như bên dưới:
}
#define MAX_ELEMENTS 100
typedef int ElementType; int deg(Graph* G, int x){
typedef struct { int dem = 0;
ElementType data[MAX_ELEMENTS]; for(int e=1; e<=G->n; e++)
int size; if(G->A[x][e]==1 )
} List; dem++;
/* Tao danh sach rong */
void make_null(List* L) { return dem;
L->size = 0; }
}
/* Them mot phan tu vao cuoi danh sach */ 4. BẬC LỚN NHẤT
void push_back(List* L, ElementType x) { Cho một đồ thị vô hướng, không khuyên, không đa
L->data[L->size] = x; cung có n đỉnh và m cung.
L->size++;
Viết chương trình tính và in ra màn hình đỉnh có bậc
}
lớn nhất và bậc tương ứng của nó. Nếu có nhiều đỉnh
/* Lay phan tu tai vi tri i, phan tu bat dau o vi tri 1 */
có bật bằng nhau thì in ra đỉnh có số thứ tự nhỏ nhất.
ElementType element_at(List* L, int i) {
return L->data[i-1]; Đầu vào (Input):
1
Dữ liệu đầu vào được nhập từ bàn // tao do thi co n dinh, m cung
phím với định dạng: void init_graph(Graph *G, int n, int m){
- Dòng đầu tiên chứa 2 số nguyên n và G->n = n;
m, tương ứng là số đỉnh và số cung. G->m = m;
for (int i = 1; i <= n; i++)
- m dòng tiếp theo mỗi dòng chứa 2 for (int j = 1; j <= m; j++)
số nguyên u v mô tả cung (u, v) G->a[i][j] = 0;
Đầu ra (Output): }
// them cung e = (x, y) vao do thi
In ra màn hình đỉnh có bậc lớn nhất và void add_edge(Graph *G, int e, int x, int y){
bậc của nó. G->a[x][e] = 1; // x lien thuoc voi e
Xem thêm ví dụ bên dưới. G->a[y][e] = 1; // y lien thuoc voi e
}
Trong ví dụ đầu tiên ta có: // kiem tra y co ke voi x k
int adjcent(Graph *G, int x, int y)
 Bậc của đỉnh 1 là 1, {
 Bậc của đỉnh 2 là 3 for (int e = 1; e <= G->m; e++)
 Bậc của đỉnh 3 là 2 if (G->a[x][e]==1 && G-
 Bậc của đỉnh 4 là 2 >a[y][e]==1)
return 1;
Vậy đỉnh có bậc lớn nhất là đỉnh 2 và bậc của nó là 3. return 0;
Vì thế ta in ra: 2 3 }
Chú ý: } Graph;

 Để chạy thử chương trình, bạn nên tạo một


tập tin dt.txt chứa đồ thị cần kiểm tra.
 Thêm dòng freopen("dt.txt", "r", stdin); vào
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
dòng này ra.
 Có thể sử dụng đoạn chương trình đọc dữ
liệu mẫu sau đây:

freopen("dt.txt", "r", stdin); //Khi nộp bài


nhớ bỏ dòng này.
Graph G;
int n, m, u, v, w, e;
scanf("%d%d", &n, &m);
init_graph(&G, n);

for (e = 0; e < m; e++) {


scanf("%d%d%d", &u, &v);
add_edge(&G, u, v);
}

// cau truc dinh_cung


#include <stdio.h>
#define max_vertices 100
#define max_edges 500
typedef struct{
int n; // so dinh
int m; // so cung
int a[max_vertices][max_edges]; // ma tran n
dinh m cung

2
// tinh bac cua dinh x
int degree(Graph *G, int x){
int deg = 0; // bien dem so bac cua
dinh x for (int e = 1; e <= G->m; e++)
if (G->a[x][e] == 1)
deg++;
return deg;
}

int main(){
// int n=7, m=9,
v; Graph G;
// init_graph(&G, n, m);
//
// add_edge(&G, 1, 1, 2);
// add_edge(&G, 2, 1, 3);
// add_edge(&G, 3, 1, 3);
// add_edge(&G, 4, 3, 6);
// add_edge(&G, 5, 1, 4);
// add_edge(&G, 6, 3, 4);
// add_edge(&G, 7, 6, 5);
// add_edge(&G, 8, 5, 6);
// add_edge(&G, 9, 4,
5); int n, m, e, u, v;
// FILE *file = fopen("vdDoThi.txt",
"r"); scanf("%d%d", &n, &m);
init_graph(&G, n, m);
for (e = 1; e <= m; e++){
scanf("%d%d", &u, &v);
add_edge(&G, e, u, v);
}
int node = 1;

3
int max = degree(&G, 1); Đầu ra (Output):
for (int i = 2; i <= G.n; i++){
if (degree(&G, i) > max){ In ra Ma trận kề (0/1) của đồ thị
node = i; Ví dụ:
max = degree(&G, Nếu đầu vào là:
i);
}
} 44
printf("%d %d", node, max); 12
return 0; 13
} 23
34
5. DANH SÁCH CUNG Kết quả là:
Cho cấu trúc dữ liệu đồ thị được cài đặt bằng 0110
phương pháp "Danh sách cung" như sau: 1010
typedef struct { 1101
int x, y; 0010
} Edge;
typedef struct { #include <stdio.h>
int n, m; typedef struct{
Edge edges[MAX_EDGES]; int n, m;
} Graph; int A[100][100];
Các cung được lưu trong danh sách edges với chỉ số } Graph;
từ 0, 1, 2, ..., m-1 void init_graph(Graph *pG, int n){
pG->n = n;
Hàm khởi tạo đồ thị: pG->m = 0;
void init_graph(Graph* G, int n){ for (int i = 1; i <= n; i++)
G->n = n; for (int j = 1; j <= n; j++)
G->m = 0; pG->A[i][j] = 0;
} }
void add_edge(Graph *pG, int u, int v){
pG->A[u][v] = 1;
Viết hàm add_edge(Graph* G, int x, int y) để thêm
pG->A[v][u] = 1;
cung (x, y) vào đồ thị G.
pG->m++;
void add_edge(Graph* G, int x, int y) { mô tả cung (u, v).
}

void add_edge(Graph *G, int x, int y){


G->edges[G->m] = (Edge){x,y};
G->m++;
}

6. ĐỌC ĐỒ THỊ TỪ TẬP TIN


Hãy viết chương trình đọc đồ thị từ tập tin và hiển
thị ma trận kề của đồ thị này.
Giả sử đồ thị được cho là đồ thị vô hướng đơn.
Đầu vào (Input):
Dữ liệu đầu vào được nhập từ tập tin dt1.txt với định
dạng:
- Dòng đầu tiên chứa 2 số nguyên n và m, tương
ứng là số đỉnh và số cung.
- m dòng tiếp theo mỗi dòng chứa 2 số nguyên u v
4
}
int main(){
freopen("./dt1.txt", "r",
stdin); int n, m;
Graph g;
scanf("%d%d", &n,
&m); init_graph(&g,
n);
for (int i = 0; i < m; i+
+){ int u, v;
scanf("%d%d", &u,
&v); add_edge(&g, u,
v);
}
//printf("Ma tran ke:\
n"); for (int i = 1; i <=
n; i++){
for (int j = 1; j <= n; j+
+) printf("%d ",
g.A[i][j]);
printf("\n");
}
}
1. DUYỆT ĐỒ THỊ

5
Cho một đồ thị vô hướng đơn. Hãy in ra thứ tự của &u, &v);
các đỉnh khi duyệt đồ thị theo chiều rộng bắt đầu add_edge(&G, u, v);
từ đỉnh 1. }
Nếu đồ thị không liên thông, sau khi duyệt xong lần
1, tìm đỉnh có chỉ số nhỏ nhất chưa duyệt mà duyệt #include <stdio.h>
nó, và cứ tiếp tục như thế cho đến khi tất cả các đỉnh #define MAX_NODE 100
đều được duyệt. typedef struct{
int data[MAX_NODE];
Quy ước:
int rear;
} Queue;
 Các đỉnh kề của 1 đỉnh được liệt kê theo thứ void makenull(Queue *pQ){
tự tăng dần pQ->rear = -1;
}
int empty(Queue q){
Đầu vào (Input): return q.rear == -1;
}
Dữ liệu đầu vào được nhập từ bàn phím với định void enqueue(int x, Queue *pQ){
dạng: pQ->rear++;
pQ->data[pQ->rear] = x;
- Dòng đầu tiên chứa 2 số nguyên n và m, tương ứng }
là số đỉnh và số cung. void dequeue(Queue *pQ){
- m dòng tiếp theo mỗi dòng chứa 2 số nguyên u v for (int i = 0; i < pQ->rear; i++)
nói rằng mô tả cung (u, v). pQ->data[i] = pQ->data[i + 1];
pQ->rear--;
Đầu ra (Output): scanf("%d%d",
In ra các đỉnh theo thứ tự duyệt, mỗi đỉnh trên 1
dòng.

Xem thêm các ví dụ bên dưới.

Hướng dẫn đọc dữ liệu và chạy thử chương trình

 Để chạy thử chương trình, bạn nên tạo một


tập tin dt.txt chứa đồ thị cần kiểm tra.
 Thêm dòng freopen("dt.txt", "r", stdin); vào
ngay sau hàm main(). Khi
nộp bài, nhớ gỡ bỏ dòng này
ra.
 Có thể sử dụng đoạn chương
trình đọc dữ liệu mẫu sau
đây:

freopen("dt.txt", "r",
stdin); //Khi nộp bài nhớ bỏ dòng
này.
Graph G;
int n, m, u, v, w, e;
scanf("%d%d", &n, &m);
init_graph(&G, n);

for (e = 0; e < m; e++) {


6
}
int front(Queue q)
{ return
q.data[0];
}
typedef
struct{ int
n, m;
int A[MAX_NODE][MAX_NODE];
} Graph;
void init_graph(Graph *pG, int
n){ pG->n = n;
pG->m = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j+
+)
pG->A[i][j] = 0;
}
void add_edge(Graph *pG, int u, int
v){ pG->A[u][v]++;
pG->A[v][u]+
+; pG->m++;
}

Graph read_graph_from_keyboard()
{ int n, m;
scanf("%d%d", &n,
&m); Graph G;
init_graph(&G,
n); for (; m != 0;
m--){
int u, v;
scanf("%d%d", &u,
&v); add_edge(&G, u,
v);

7
}  Các đỉnh kề của 1 đỉnh được liệt kê theo thứ
return G; tự tăng dần.
}
Graph read_graph_from_file(char *filepath){
FILE *f = freopen(filepath, "r", stdin);
Graph G = read_graph_from_keyboard(); Đầu vào (Input):
fclose(f);
return G; Dữ liệu đầu vào được nhập từ bàn
} phím với định dạng:
int marked[MAX_NODE]; - Dòng đầu tiên chứa 2 số nguyên n
void initial(){ và m, tương ứng là số đỉnh và số
for (int i = 0; i < MAX_NODE; i++) cung.
marked[i] = 0;
} - m dòng tiếp theo mỗi dòng chứa 2
void BFS(Graph *pG, int u){ số nguyên u v nói rằng mô tả cung
Queue q; (u, v).
makenull(&q); Đầu ra (Output):
enqueue(u, &q);
while (!empty(q)){ In ra các đỉnh theo thứ tự duyệt, mỗi
u = front(q); đỉnh trên 1 dòng.
dequeue(&q); Xem thêm các ví dụ bên dưới.
if (marked[u])
continue;
marked[u] = 1; Hướng dẫn đọc dữ liệu và chạy thử chương trình
printf("%d\n", u);
for (int i = 1; i <= pG->n; i++){  Để chạy thử chương trình, bạn nên tạo một
if (!pG->A[u][i]) tập tin dt.txt chứa đồ thị cần kiểm tra.
continue;  Thêm dòng freopen("dt.txt", "r", stdin); vào
enqueue(i, &q); ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
} dòng này ra.
}  Có thể sử dụng đoạn chương trình đọc dữ
} liệu mẫu sau đây:
int main(){
// Graph G = read_graph_from_file((char freopen("dt.txt", "r", stdin); //Khi nộp bài
*)"./graph.txt"); nhớ bỏ dòng này.
Graph G = read_graph_from_keyboard(); Graph G;
initial(); int n, m, u, v, w, e;
for (int i = 1; i <= G.n; i++) scanf("%d%d", &n, &m);
if (!marked[i]) init_graph(&G, n);
BFS(&G, i);
return 0; for (e = 0; e < m; e++) {
} scanf("%d%d", &u, &v);
add_edge(&G, u, v);
}
Cho một đồ thị vô hướng đơn. Hãy in ra thứ tự của
các đỉnh khi duyệt đồ thị theo chiều sâu (sử dụng ĐỆ #include <stdio.h>
QUY) bắt đầu từ đỉnh 1. #define MAX_NODE 100
Nếu đồ thị không liên thông, sau khi duyệt xong lần typedef struct{
1, tìm đỉnh có chỉ số nhỏ nhất chưa duyệt mà duyệt int n, m;
nó, và cứ tiếp tục như thế cho đến khi tất cả các đỉnh int A[MAX_NODE][MAX_NODE];
đều được duyệt. } Graph;
typedef struct{
Quy ước: int data[MAX_NODE];
int top;
8
} Stack; marked[u] = 1;
printf("%d\n", u);
void makenull(Stack *pS){ for (int i = 1; i <= pG->n; i++){
pS->top = -1; if (i == u || !pG->A[u][i])
} continue;
int empty(Stack s){ DFS(pG, i);
return s.top == -1; }
} }
void push(int x, Stack *pS){ int main(){
pS->top++; // Graph G = read_graph_from_file((char
pS->data[pS->top] = x; *)"./graph.txt");
} Graph G = read_graph_from_keyboard();
int pop(Stack *pS){ initial();
pS->top--; for (int i = 1; i <= G.n; i++)
return pS->data[pS->top + 1]; DFS(&G, i);
} return 0;
void init_graph(Graph *pG, int n){ }
pG->n = n;
pG->m = 0;
for (int i = 1; i <= n; i++) Cho một đồ thị vô hướng đơn. Hãy in ra thứ tự của
for (int j = 1; j <= n; j++) các đỉnh khi duyệt đồ thị theo chiều sâu (sử dụng
pG->A[i][j] = 0; NGĂN XẾP) bắt đầu từ đỉnh 1.
}
void add_edge(Graph *pG, int u, int v){ Nếu đồ thị không liên thông, sau khi duyệt xong lần
pG->A[u][v]++; 1, tìm đỉnh có chỉ số nhỏ nhất chưa duyệt mà duyệt
pG->A[v][u]++; nó, và cứ tiếp tục như thế cho đến khi tất cả các đỉnh
pG->m++; đều được duyệt.
} Quy ước:
Graph read_graph_from_keyboard(){
int n, m;  Các đỉnh kề của 1 đỉnh được liệt kê theo thứ
scanf("%d%d", &n, &m); tự tăng dần.
Graph G;
init_graph(&G, n);
for (; m != 0; m--){
int u, v; Đầu vào (Input):
scanf("%d%d", &u, &v);
add_edge(&G, u, v); Dữ liệu đầu vào được nhập từ bàn phím với định
} dạng:
return G; - Dòng đầu tiên chứa 2 số nguyên n và m, tương ứng
} là số đỉnh và số cung.
Graph read_graph_from_file(char *filepath){
FILE *f = freopen(filepath, "r", stdin); - m dòng tiếp theo mỗi dòng chứa 2 số nguyên u v
Graph G = read_graph_from_keyboard(); nói rằng mô tả cung (u, v).
fclose(f); Đầu ra (Output):
return G;
} In ra các đỉnh theo thứ tự duyệt, mỗi đỉnh trên 1
int marked[MAX_NODE]; dòng.
void initial(){ Xem thêm các ví dụ bên dưới.
for (int i = 0; i < MAX_NODE; i++)
marked[i] = 0;
} Hướng dẫn đọc dữ liệu và chạy thử chương trình
void DFS(Graph *pG, int u){
if (marked[u])  Để chạy thử chương trình, bạn nên tạo một
return; tập tin dt.txt chứa đồ thị cần kiểm tra.
9
 Thêm dòng freopen("dt.txt", }
"r", stdin); vào ngay sau void push(Stack *S, int x){
hàm main(). Khi nộp bài, S->data[S->size] = x;
nhớ gỡ bỏ dòng này ra. S->size++;
 Có thể sử dụng đoạn chương }
trình đọc dữ liệu mẫu sau int top(Stack *S){
đây: return S->data[S->size - 1];
}
freopen("dt.txt", "r", void pop(Stack *S){
stdin); //Khi nộp bài nhớ bỏ dòng S->size--;
này. }
Graph G; int empty(Stack *S){
int n, m, u, v, w, e; return S->size == 0;
scanf("%d%d", &n, &m); }
init_graph(&G, n); // Khoi tao do thi G co n dinh
void init_graph(Graph *G, int n){
for (e = 0; e < m; e++) { int i, j;
scanf("%d%d", &u, &v); G->n = n;
add_edge(&G, u, v); G->m = 0;
} for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
G->a[i][j] = 0;
#include <stdio.h> }
typedef struct{ // Them cung e = (x, y) vao do thi G
int n, m; // n dinh, m canh void add_edge(Graph *G, int e, int x, int y){
int a[100][500]; G->a[x][e] = 1; // x lien thuoc voi e
} Graph; G->a[y][e] = 1;
typedef int ElementType; G->m++;
typedef struct{ }
ElementType data[100]; // void add_edge(Graph *G, int x, int y)
int size; // {
} List; // G->a[x][y] = 1; // y ke voi x
void makenullList(List *L){ // G->a[y][x] = 1; // x ke voi y
L->size = 0; // }
} // Kiem tra y co ke voi x khong
// Them mot phan tu vao cuoi danh sach int adjacent(Graph *G, int x, int y){
void push_back(List *L, ElementType x){ int e;
L->data[L->size] = x; for (e = 1; e <= G->m; e++)
L->size++; if (G->a[x][e] && G->a[y][e])
} return 1;
// Lay phan tu tai vi tri i, phan tu bat dau o vi tri 1 return 0;
ElementType element_at(List *L, int i){ }
return L->data[i - 1]; // Tim cac dinh ke cua dinh x
} List neighbors(Graph *G, int x){
// Tra ve so phan tu cua danh sach // Graph G;
int count_list(List *L){ int y;
return L->size; List L;
} makenullList(&L);
typedef struct{ for (y = 1; y <= G->n; y++)
int data[100]; if (adjacent(G, x, y))
int size; push_back(&L, y);
} Stack; return L;
void makenullStack(Stack *S){ }
S->size = 0; int mark[100];
10
// duyet do thi theo chieu sau Nếu có thể đi được in ra màn hình YES, ngược lại in
void depth_first_search(Graph *G, int x){ ra NO.
if (mark[x])
return; printf("%d\ Xem thêm ví dụ bên dưới.
n", x); mark[x] =
!0; #include <stdio.h>
for (int i = G->n; i >= 1; i--) #define MAX_NODE 100
if (adjacent(G, x, i)) typedef int ELEMENT_TYPE;
depth_first_search(G, i); typedef struct{
} ELEMENT_TYPE data[MAX_NODE];
int main(){ int top;
Graph G; } Stack;
// freopen("vdDoThi.txt", "r", stdin); void makenull(Stack *pS){
int n, m, u, v, e; pS->top = -1;
scanf("%d%d", &n, &m); }
init_graph(&G, n); int empty(Stack s){
for (e = 0; e < m; e++){ scanf("%d return s.top == -1;
%d", &u, &v); add_edge(&G, e + }
1, u, v); void push(ELEMENT_TYPE x, Stack *pS){
} pS->top++;
for (int i = 1; i <= G.n; i++) pS->data[pS->top] = x;
mark[i] = 0; }
for (int i = 1; i <= G.n; i++)
depth_first_search(&G, i); ELEMENT_TYPE pop(Stack *pS){
return 0; pS->top--;
} return pS->data[pS->top + 1];
}
typedef struct{
2. DUYỆT ĐỒ THỊ VÀ DỰNG CÂY DUYỆT ĐỒ int n, m;
THỊ int A[MAX_NODE][MAX_NODE];
} Graph;
void init_graph(Graph *pG, int n){
pG->n = n;
pG->m = 0;
for (int i = 1; i <= n; i++)
3. ĐỒ THỊ LIÊN THÔNG QUA ĐẢO for (int j = 1; j <= n; j++)
pG->A[i][j] = 0;
Có n hòn đảo và m cây cầu. Mỗi cây cầu bắt qua 2 }
hòn đảo. Một hôm chúa đảo tự hỏi là với các cây cầu void add_edge(Graph *pG, int u, int v){
hiện tại thì đứng ở một hòn đảo bất kỳ có thể nào đi pG->A[u][v]++;
đến được tất cả các hòn đảo khác hay không. pG->A[v][u]++;
Hãy giúp chúa đảo viết chương trình kiểm tra. pG->m++;
}
Đầu vào (Input): Graph read_graph_from_keyboard(){
Dữ liệu đầu vào được nhập từ bàn phím với định int n, m;
dạng: scanf("%d%d", &n, &m);
Graph G;
- Dòng đầu tiên chứa 2 số nguyên n và m, tương init_graph(&G, n);
ứng là số đảo và số cây cầu. for (; m != 0; m--) {
- m dòng tiếp theo mỗi dòng chứa 2 số nguyên u v nói int u, v;
rằng có 1 cây cầu bắt qua hai hòn đảo u và v. scanf("%d%d", &u, &v);
add_edge(&G, u, v);
Đầu ra (Output):
}
return G;
11
} qua một cây kế tiếp hái trái của này và tiếp tục như
Graph read_graph_from_file(char *filepath){ thế cho đến khi tất cả các cây đều được hái trái. Một
FILE *f = freopen(filepath, "r", stdin); cây có thể được chuyền qua chuyền lại nhiều lần.
Graph G = read_graph_from_keyboard();
fclose(f); Hãy giúp Tôn Ngộ Không kiểm tra xem kế hoạch này
return G; có thể thực hiện được không.
} Đầu vào (Input):
#define TRUE 1
#define FALSE 0 Giả sử số lượng cây ăn trái ở Hoa Quả Sơn là n cây
int marked[MAX_NODE]; và được đánh số từ 1 đến n.
int countmarked;
void initialarray(int *arr, int initialvalue){ Dữ liệu đầu vào được nhập từ
for (int i = 0; i < MAX_NODE; i++) bàn phím với định dạng:
*(arr + i) = initialvalue; - Dòng đầu tiên chứa 2 số
} nguyên n và m, tương ứng là số
void initial(){ cây và số cặp cây có thể chuyền
initialarray(marked, FALSE); qua lại.
countmarked = 0;
} - m dòng tiếp theo mỗi dòng
void DFS(Graph *pG, int u){ chứa 2 số nguyên u v nói rằng có
if (marked[u]) thể chuyền từ cây u sang cây v
return; hoặc chuyền từ cây v sang cây u.
countmarked++; Đầu ra (Output):
marked[u] = TRUE;
Nếu kế hoạch của Tôn Ngộ
for (int i = 1; i <= pG->n; i++)
Không có thể thực hiện được DUOC, ngược lại in
if (pG->A[u][i])
ra KHONG.
DFS(pG, i);
} Xem thêm ví dụ bên dưới. Trong ví dụ đầu tiên, Tôn
Ngộ Không bắt đầu từ cây 1, chuyền qua cây 2, sau
int main(){ đó chuyền ngược về 1, chuyền tiếp sang 3 và sau cùng
// Graph G = read_graph_from_file((char là sang 4.
*)"./graph.txt");
#include <stdio.h>
Graph G = read_graph_from_keyboard();
#define INFINITY 9999
initial();
#define MAXN 500
DFS(&G, 1);
#define NO_EDGE 0
printf("%s", countmarked == G.n ? "YES" : "NO");
typedef struct {
return 0;
int n; // Dinh
}
int m;
int L[MAXN][MAXN];
} Graph;
4. TÔN NGỘ KHÔNG
void init_graph(Graph* G, int n, int m) {
Tôn Ngộ Không là một trong các nhân vật chính của
G->n = n;
truyện Tây du ký. Khi còn ở Hoa Quả Sơn, Tôn Ngộ G->m = m;
Không là vua của loài khỉ. Hoa Quả Sơn có rất nhiều
int i, j;
cây ăn trái, nên loài khỉ rất thích. Do đặc tính của
for (i = 1; i <= n; i++) {
mình, khỉ không thích đi bộ mà chỉ thích chuyền từ
for (j = 1; j <= m; j++) {
cây này sang một cây khác. Tuy nhiên, nếu khoảng
G->L[i][j] = NO_EDGE;
cách giữa hai cây quá xa thi chúng không thể chuyền
}
qua lại được. }
Đường đường là vua của loài khỉ, Tôn Ngộ Không }
muốn vạch ra một kế hoạch hái trái cây trên tất cả #include <stdio.h>
các cây có trên Hoa Quả Sơn mà không cần phải #define MAX_ELEMENTS 100
nhảy xuống đất. Tôn Ngộ Không dự định sẽ bắt đầu // STACK
leo lên một cây, hái trái của cây này, sau đó chuyền typedef struct {
12
int data[MAX_ELEMENTS]; int i;
int size; for (i = 1; i <= L.size; i++) {
} Stack; printf("%d ", element_at(&L, i));
void make_null_stack(Stack* S) { }
S->size = 0; }
} List neighbors(Graph* G, int x) {
void push(Stack* S, int x) { List ansList;
S->data[S->size] = x; make_null(&ansList);
S->size++; int col, row, temp;
} for(col = 1; col <= G->m; col++) {
int top(Stack* S) { if(G->L[x][col] == 1) {
return S->data[S->size - 1]; for(row = 1; row <= G->n; row++) {
} if(G->L[row][col] == 1 && row != x) {
void pop(Stack* S) { push_back(&ansList, row);
S->size--; }
} }
int empty(Stack* S) { }
return S->size == 0; }
} int i, j;
// LIST for(i = 1; i <= count_list(&ansList); i++) {
#define MAX_ELEMENTS 100 for(j = i+1; j <= count_list(&ansList)-1; j++) {
typedef int ElementType; if(element_at(&ansList, i) >
typedef struct { element_at(&ansList, j)) {
ElementType data[MAX_ELEMENTS]; temp = ansList.data[i-1];
int size; ansList.data[i-1] = ansList.data[j-1];
} List; ansList.data[j-1] = temp;
void make_null(List* L) { }
L->size = 0; }
} }
void push_back(List* L, ElementType x) { List checkList;
L->data[L->size] = x; make_null(&checkList);
L->size++; int check = 1;
} for(i = 1; i <= count_list(&ansList); i++) {
ElementType element_at(List* L, int i) { for(j = 1; j <= count_list(&checkList); j++) {
return L->data[i-1]; if(element_at(&ansList, i) ==
} element_at(&checkList, j)) {
int count_list(List* L) { check = 0;
return L->size; }
} }
int adjeacent(Graph* G, int x, int y) { if(check) {
int e; push_back(&checkList, element_at(&ansList, i));
for (e = 1; e <= G->m; e++) }
if (G->L[x][e] == 1 && G->L[y][e] == 1) }
return 1;
return 0; return checkList;
} }
int degree(Graph* G, int x) { void add_edge(Graph* G, int e, int x, int y) {
int e, deg = 0; G->L[x][e] = 1;
for (e = 1; e <= G->m; e++) G->L[y][e] = 1;
if (G->L[x][e] == 1) }
deg++; #define MAX_VERTICES 500
return deg; int mark[MAX_VERTICES];
} // Duyet do thi theo chieu sau
void print_list(List L) { void dfs(Graph* G) {
13
Stack L; Cho G=<V, E> là một đồ thị vô hướng đơn (không có
make_null_stack(&L); khuyên, không có đa cung). Hãy viết chương trình
// Khoi tao mark, chua dinh nao duoc duyet kiểm tra xem có chứa chu trình hay không.
int j;
for (j = 1; j <= G->n; j++) { Chu trình là một đường đi đơn cung có đỉnh đầu
mark[j] = 0; trùng với đỉnh cuối.
} Đầu vào (Input):
// Dua 1 vao L, bat dau duyet tu dinh 1
push(&L, 1); Dữ liệu đầu vào được nhập từ bàn phím với định
while (!empty(&L)) { dạng:
int x = top(&L); - Dòng đầu tiên chứa 2 số nguyên n và m, tương ứng
pop(&L); là số đỉnh và số cung.
if (mark[x] != 0)
continue; - m dòng tiếp theo mỗi dòng chứa 2 số nguyên u, v
mark[x] = 1; mô tả cung (u, v).
List list = neighbors(G, x); Đầu ra (Output):
for (j = 1; j <= list.size; j++) {
In ra màn hình YES nếu đồ thị có chứa chu trình,
int y = element_at(&list, j);
ngược lại in ra NO
push(&L, y);
} Xem thêm ví dụ bên dưới.
}
}
int interconnected(Graph* G) { Hướng dẫn đọc dữ liệu và chạy thử chương trình
int i;
dfs(G);  Để chạy thử chương trình, bạn nên tạo một
for (i = 1; i <= G->n; i++) { tập tin dt.txt chứa đồ thị cần kiểm tra.
if (!mark[i]) {  Thêm dòng freopen("dt.txt", "r", stdin); vào
return 0; ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
break; dòng này ra.
}  Có thể sử dụng đoạn chương trình đọc dữ
} liệu mẫu sau đây:
return 1;
} freopen("dt.txt", "r", stdin);
int main() { //Khi nộp bài nhớ bỏ dòng này.
// Kiem tra ton tai chu trinh doi voi do thi vo huong Graph G;
Graph G; int n, m, u, v, w, e;
int n, m; scanf("%d%d", &n, &m);
int e, u, v; init_graph(&G, n);
// freopen("dt.txt","r",stdin);
scanf("%d%d", &n, &m); for (e = 0; e < m; e++) {
init_graph(&G, n, m); scanf("%d%d",
for (e = 1; e <= m; e++) { &u, &v);
scanf("%d%d", &u, &v); add_edge(&G, u, v);
add_edge(&G, e, u, v); }
}
if (interconnected(&G)) #include <stdio.h>
printf("DUOC"); #define MAX_NODE 100
else typedef struct{
printf("KHONG"); int n, m;
return 0; int A[MAX_NODE][MAX_NODE];
} } Graph;
typedef struct{
5. KIỂM TRA CHU TRÌNH – ĐỒ THỊ VÔ int data[MAX_NODE];
HƯỚNG int top;
14
} Stack; marked[u] = GRAY;
void makenull(Stack *pS){ parent[u] = p;
pS->top = -1; for (int i = 1; i <= pG->n; i++)
} if (pG->A[u][i] && u != i) {
int empty(Stack s){ if (has_circle)
return s.top == -1; return;
} if (i == p)
void push(int x, Stack *pS){ continue;
pS->top++; if (marked[i] == WHITE){
pS->data[pS->top] = x; DFS(pG, i, u);
} continue;
int pop(Stack *pS){ }
pS->top--; has_circle = 1;
return pS->data[pS->top + 1]; last_circle = u;
} first_circle = i;
void init_graph(Graph *pG, int n){ return;
pG->n = n; }
pG->m = 0; marked[u] = BLACK;
for (int i = 1; i <= n; i++) }
for (int j = 1; j <= n; j++)
pG->A[i][j] = 0; void print_circle(){
} Stack s;
void add_edge(Graph *pG, int u, int v){ makenull(&s);
pG->A[u][v]++; int p = last_circle;
pG->A[v][u]++; while (p != -1){
pG->m++; push(p, &s);
} if (p == first_circle)
Graph read_graph_from_keyboard(){ break;
int n, m; p = parent[p];
scanf("%d%d", &n, &m); }
Graph G; while (!empty(s)) {
init_graph(&G, n); printf("%d ", pop(&s));
for (; m != 0; m--){ }
int u, v; printf("%d\n", first_circle);
scanf("%d%d", &u, &v); }
add_edge(&G, u, v); void initial(int n){
} for (int i = 1; i <= n; i++)
return G; parent[i] = 0;
} for (int i = 1; i <= n; i++)
Graph read_graph_from_file(char *filepath){ marked[i] = WHITE;
FILE *f = freopen(filepath, "r", stdin); has_circle = 0;
Graph G = read_graph_from_keyboard(); }
fclose(f); int main(){
return G; // Graph G = read_graph_from_file((char
} *)"./graph.txt");
#define WHITE 0 Graph G = read_graph_from_keyboard();
#define GRAY 1 initial(G.n);
#define BLACK 2 for (int i = 1; i <= G.n && !has_circle; i++){
int parent[MAX_NODE]; initial(G.n);
int marked[MAX_NODE]; DFS(&G, i, -1);
int last_circle; if (has_circle)
int first_circle; printf("YES");
int has_circle; }
void DFS(Graph *pG, int u, int p){ if (!has_circle)
15
printf("NO"); Để đơn giản ta có thể giả sử các thức uống được mã
return 0; hoá thành các số nguyên từ 1, 2, …
} và dữ liệu đầu vào được cho có
dạng như sau (ví dụ 1):
6. THUYỀN TRƯỞNG HANDOK 32
Thuyền trưởng Haddock (truyện Tintin) là một 12
người luôn say xỉn. Vì thế đôi khi Tintin không biết 32
ông ta đang say hay tỉnh. Một ngày nọ, Tintin hỏi ông Có loại thức uống (soda: 1, wine: 2
ta về cách uống. Haddock nói như thế này: Có nhiều và water: 3) và có 2 điều kiện tiên
loại thức uống (soda, wine, water, …), tuy nhiên quyết
Haddock lại tuân theo quy tắc “để uống một loại thức 1 -> 2 và 3 -> 2.
uống nào đó cần phải uống tất cả các loại thức uống
tiên quyết của nó”. Ví dụ: để uống rượu (wine), Với ví dụ 2, ta có dữ liệu:
Haddock cần phải uống soda và nước (water) trước. 33
Vì thế muốn say cũng không phải dễ ! 12
Cho danh sách các thức uống và các thức uống tiên 32
quyết của nó. Hãy xét xem Haddock có thể nào say 23
không ? Để làm cho Haddock say, ông ta phải uống Viết chương trình đọc dữ liệu các thức uống và kiểm
hết tất cả các thức uống. tra xem Haddock có thể say không. Nếu có in ra
“YES”, ngược lại in ra “NO”.
Ví dụ 1:
Đầu vào (Input):
soda wine
water wine Dữ liệu đầu vào được nhập từ bàn phím (stdin) với
Thức uống tiên quyết được cho dưới dạng a b, có định dạng:
nghĩa là để uống b bạn phải uống a trước. Trong ví
dụ trên để uống wine, Haddock phải uống soda và  Dòng đầu tiên chứa 2 số nguyên n và m,
water trước. Soda và water không có thức uống tiên tương ứng là số thức uống và số điều kiện tiên
quyết nên Haddock sẽ SAY. quyết .
 m dòng tiếp theo mỗi dòng chứa 2 số
Ví dụ 2: nguyên u v nói rằng thức uống u là tiên quyết
soda wine của thức uống v.
water wine
wine water Đầu ra (Output):
Để uống wine, cần uống water. Tuy nhiên để uống
water lại cần wine. Vì thế Haddock không thể uống  Nếu Haddock có thể say in ra YES, ngược lại
hết được các thức uống nên ông ta KHÔNG SAY. in ra NO.

#include <stdio.h>
#define INFINITY 9999
#define MAXN 500
#define NO_EDGE 0
typedef struct {
int n; // Dinh
int m;
int L[MAXN][MAXN];
} Graph;
void init_graph(Graph* G, int n, int m) {
G->n = n;
G->m = m;
int i, j;
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
G->L[i][j] = NO_EDGE;
16
} for(j = i+1; j <= count_list(&ansList)-1; j++) {
} if(element_at(&ansList, i) >
} element_at(&ansList, j)) {
// LIST temp = ansList.data[i-1];
#define MAX_ELEMENTS 100 ansList.data[i-1] = ansList.data[j-1];
typedef int ElementType; ansList.data[j-1] = temp;
typedef struct { }
ElementType data[MAX_ELEMENTS]; }
int size; }
} List; List checkList;
void make_null(List* L) { make_null(&checkList);
L->size = 0; int check = 1;
} for(i = 1; i <= count_list(&ansList); i++) {
void push_back(List* L, ElementType x) { for(j = 1; j <= count_list(&checkList); j++) {
L->data[L->size] = x; if(element_at(&ansList, i) ==
L->size++; element_at(&checkList, j)) {
} check = 0;
ElementType element_at(List* L, int i) { }
return L->data[i-1]; }
} if(check) {
int count_list(List* L) { push_back(&checkList, element_at(&ansList, i));
return L->size; }
} }
int adjeacent(Graph* G, int x, int y) {
int e; return checkList;
for (e = 1; e <= G->m; e++) }
if (G->L[x][e] == 1 && G->L[y][e] == 1) void add_edge(Graph* G, int x, int y) {
return 1; G->L[x][y] = 1;
return 0; }
} #define white 0
int degree(Graph* G, int x) { #define black 1
int e, deg = 0; #define gray 2
for (e = 1; e <= G->m; e++) #define MAX_VERTICES 1000
if (G->L[x][e] == 1) int color[MAX_VERTICES];
deg++; int cycle;
return deg; // Kiem tra chu trinh doi voi do thi co huong
} // Duyet do thi bat dau tu dinh x
void print_list(List L) { void dfs(Graph* G, int x) {
int i; color[x] = gray;
for (i = 1; i <= L.size; i++) { int j;
printf("%d ", element_at(&L, i)); List list = neighbors(G, x);
} for (j = 1; j <= list.size; j++) {
} int y = element_at(&list, j);
List neighbors(Graph* G, int x) { if (color[y] == gray) {
List ansList; cycle = 1; // Ton tai chu trinh
make_null(&ansList); return;
int col, temp; }
for(col = 1; col <= G->n; col++) { if (color[y] == white) {
if (G->L[x][col] && col != x) { dfs(G, y);
push_back(&ansList, col); }
} }
} color[x] = black;
int i, j; }
for(i = 1; i <= count_list(&ansList); i++) { // Kiem tra toan bo do thi

17
int contains_cycle(Graph* G) {  M dòng tiếp theo mỗi dòng chứa 2 số
int j; nguyên u v nói rằng 2 thành viên u và v đã
for (j = 1; j <= G->n; j++) { từng thi đấu chung với nhau.
color[j] = white;
} Đầu ra (Output):
cycle = 0;
for (j = 1; j <= G->n; j++) {
 Nếu phân chia được, hãy
if (color[j] == white) {
in ra các thành viên của
dfs(G, j);
mỗi nhóm. Nhóm của
}
thành viên 1 sẽ được in
}
trước, nhóm còn lại in ra
return cycle;
sau. Các thành viên trong
}
nhóm được in ra theo thứ
int main() {
tự tăng dần và in trên 1
Graph G;
dòng. Hai thành viên cách
int e, u, v, m, n;
nhau 1 khoảng trắng.
// freopen("dt.txt","r",stdin);
 Nếu không thể phân chia,
scanf("%d%d", &n, &m);
in ra IMPOSSIBLE
init_graph(&G, n, m);
for (e = 1; e <= m; e++) {
scanf("%d%d", &u, &v); #include <stdio.h>
add_edge(&G, u, v); #define INFINITY 9999
} #define MAXN 500
if (contains_cycle(&G)) #define NO_EDGE 0
printf("NO"); typedef struct {
else int n; // Dinh
printf("YES"); int m;
return 0; int L[MAXN][MAXN];
} } Graph;
void init_graph(Graph* G, int n, int m) {
7. PHÂN CHIA ĐỘI BÓNG G->n = n;
David là huấn luyện viên của một đội bóng G->m = m;
gồm N thành viên. David muốn chia đội bóng thành int i, j;
hai nhóm. Để tăng tính đa dạng của các thành viên for (i = 1; i <= n; i++) {
trong nhóm, David quyết định không xếp hai thành for (j = 1; j <= m; j++) {
viên đã từng thi đấu với nhau vào chung một nhóm. G->L[i][j] = NO_EDGE;
Bạn hãy lập trình giúp David phân chia đội bóng. }
}
Đầu vào (Input): }
// LIST
Dữ liệu đầu vào được nhập từ bàn phím với định #define MAX_ELEMENTS 100
dạng: typedef int ElementType;
typedef struct {
 Dòng đầu tiên chứa 2 số nguyên N và M, ElementType data[MAX_ELEMENTS];
tương ứng là số thành viên và số cặp thành int size;
viên đã từng thi đấu với nhau. } List;
void make_null(List* L) {
L->size = 0;
}
void push_back(List* L, ElementType x) {
L->data[L->size] = x;
L->size++;
}
ElementType element_at(List* L, int i) {
return L->data[i-1];
18
} if(element_at(&ansList, i) ==
int count_list(List* L) { element_at(&checkList, j)) {
return L->size; check = 0;
} }
int adjeacent(Graph* G, int x, int y) { }
int e; if(check) {
for (e = 1; e <= G->m; e++) push_back(&checkList, element_at(&ansList, i));
if (G->L[x][e] == 1 && G->L[y][e] == 1) }
return 1; }
return 0;
} return checkList;
int degree(Graph* G, int x) { }
int e, deg = 0; void add_edge(Graph* G, int e, int x, int y) {
for (e = 1; e <= G->m; e++) G->L[x][e] = 1;
if (G->L[x][e] == 1) G->L[y][e] = 1;
deg++; }
return deg; #define MAX_VERTICES 1000
} int color[MAX_VERTICES];
void print_list(List L) { int fail;
int i; // To mau dinh bang phuong phap de quy
for (i = 1; i <= L.size; i++) { void colorize(Graph* G, int x, int c) {
printf("%d ", element_at(&L, i)); // Neu dinh x da chua co mau => to no
} if (color[x] == -1) {
} color[x] = c;
List neighbors(Graph* G, int x) { List list = neighbors(G, x);
List ansList; int j;
make_null(&ansList); for (j = 1; j <= list.size; j++) {
int col, row, temp; int y = element_at(&list, j);
for(col = 1; col <= G->m; col++) { colorize(G, y, !c);
if(G->L[x][col] == 1) { }
for(row = 1; row <= G->n; row++) { } else {
if(G->L[row][col] == 1 && row != x) { if (color[x] != c) // 1 dinh bi to 2 mau khac nhau
push_back(&ansList, row); fail = 1;
} }
} }
} int is_bigraph(Graph* G) {
} // Khoi tao color, chua dinh nao co mau
int i, j; int j;
for(i = 1; i <= count_list(&ansList); i++) { for (j = 1; j <= G->n; j++) {
for(j = i+1; j <= count_list(&ansList)-1; j++) { color[j] = -1;
if(element_at(&ansList, i) > }
element_at(&ansList, j)) { fail = 0;
temp = ansList.data[i-1]; colorize(G, 1, 0); // To mau dinh 1 bang mau den
ansList.data[i-1] = ansList.data[j-1]; return !fail;
ansList.data[j-1] = temp; }
} int main() {
} // Kiem tra ton tai chu trinh doi voi do thi vo huong
} Graph G;
List checkList; int e, u, v, n, m;
make_null(&checkList); // freopen("dt.txt","r",stdin);
int check = 1; scanf("%d%d", &n, &m);
for(i = 1; i <= count_list(&ansList); i++) { init_graph(&G, n, m);
for(j = 1; j <= count_list(&checkList); j++) { for (e = 1; e <= m; e++) {
scanf("%d%d", &u, &v);
19
add_edge(&G, e, u, v);  Thêm dòng freopen("dt.txt", "r", stdin); vào
} ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
if (is_bigraph(&G)) { dòng này ra.
int i;  Có thể sử dụng đoạn chương
for (i = 1; i <= G.n; i++) { trình đọc dữ liệu mẫu sau
if (!color[i]) { đây:
printf("%d ", i);
} freopen("dt.txt", "r",
} stdin); //Khi nộp bài nhớ bỏ dòng
printf("\n"); này.
for (i = 1; i <= G.n; i++) { Graph G;
if (color[i]) { int n, m, u, v, w, e;
printf("%d ", i); scanf("%d%d", &n, &m);
}
init_graph(&G, n);
}
} for (e = 0; e < m; e++) {
else
printf("IMPOSSIBLE"); scanf("%d%d%d", &u,
return 0; &v, &c);
} add_edge(&G, u,
v, w);
}

1. TÌM ĐƯỜNG ĐI NGẮN NHẤT


#include <stdio.h>
Cho đồ thị có hướng G = <V, E> có n đỉnh và m cung
(n < 100, m < 500). Mỗi cung được gán một trọng số #define MAX_VERTICES 50
w (0 < w <= 100). #define MAX_EDGES 50
Viết chương trình tìm đường đi ngắn nhất từ đỉnh 1
đến n. #define NO_EDGE -1

Đầu vào (Input): typedef struct {


Dữ liệu đầu vào được nhập từ bàn phím với định int n, m;
dạng:
int A[MAX_VERTICES][MAX_EDGES];
- Dòng đầu tiên chứa 2 số nguyên n và m.
- m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v, } Graph;
w mô tả cung (u, v) có trọng số w. void init_graph(Graph* G, int n, int m) {
Đầu ra (Output):
int i, j;
In ra màn hình chiều dài của đường đi ngắn nhất từ
1 đến n. Nếu không có đường đi từ 1 đến n, in ra -1. G->n = n;
Xem thêm ví dụ bên dưới. G->m = m;
Chú ý: for (i = 1; i <= n; i++)

 Để chạy thử chương trình, bạn nên tạo một for (j = 1; j <= m; j++)
tập tin dt.txt chứa đồ thị cần kiểm tra.
G->A[i][j] = NO_EDGE;
}
void add_edge(Graph* G, int x, int y, int w) {
G->A[x][y] = w;
20
G->A[y][x] = w; // freopen("dt.txt", "r", stdin);
} Graph G;
#define INFINITY 9999999 int n, m, u, v, e, w;
int mark[MAX_VERTICES]; scanf("%d%d", &n, &m);
int pi[MAX_VERTICES]; init_graph(&G, n, m);
int p[MAX_VERTICES]; for (e = 1; e <= m; e++) {
void Dijkstra(Graph* G, int s) { scanf("%d%d%d", &u, &v, &w);
int i, j, it; add_edge(&G,u,v,w);
for (i = 1; i <= G->n; i++) { }
pi[i] = INFINITY; Dijkstra(&G, 1);
mark[i] = 0; if(pi[n]>0){
} printf("%d", pi[n]);
pi[s] = 0; }
p[s] = -1; else printf("-1");
for (it = 1; it < G->n; it++) { return 0;
int min_pi = INFINITY; }
for (j = 1; j <= G->n; j++)
if (mark[j] == 0 && pi[j] < min_pi) { Cho đồ thị vô hướng G = <V, E> có n đỉnh và m cung
min_pi = pi[j]; (n < 100, m < 500). Mỗi cung được gán một trọng số
w (0 < w <= 100).
i = j; Viết chương trình tìm đường đi ngắn nhất từ đỉnh 1
} đến n.
Đầu vào (Input):
mark[i] = 1;
Dữ liệu đầu vào được nhập từ bàn phím với định
for (j = 1; j <= G->n; j++) dạng:
if (G->A[i][j] != NO_EDGE && mark[j] == 0) { - Dòng đầu tiên chứa 2 số nguyên n và m.

if (pi[i] + G->A[i][j] < pi[j]) { - m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v, w
mô tả cung (u, v) có trọng số w.
pi[j] = pi[i] + G->A[i][j];
Đầu ra (Output):
p[j] = i; In ra màn hình chiều dài của đường đi ngắn nhất từ
1 đến n. Nếu không có đường đi từ 1 đến n, in ra -1.
}
Xem thêm ví dụ bên dưới.
}
Chú ý:
}
 Để chạy thử chương trình, bạn nên tạo một
} tập tin dt.txt chứa đồ thị cần kiểm tra.
int main() {
21
 Thêm dòng freopen("dt.txt", "r", stdin); vào int mark[MAX_VERTICES];
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
dòng này ra. int pi[MAX_VERTICES];
 Có thể sử dụng đoạn chương trình đọc dữ
liệu mẫu sau đây: int p[MAX_VERTICES];

freopen("dt.txt", "r", stdin);


//Khi nộp bài nhớ bỏ dòng này. void Dijkstra(Graph* G, int s) {
Graph G;
int n, m, u, v, w, e; int i, j, it;
scanf("%d%d", &n, &m);
for (i = 1; i <= G->n; i++) {
init_graph(&G, n);
pi[i] = INFINITY;
for (e = 0; e < m; e++) {
scanf("%d%d%d", mark[i] = 0;
&u, &v, &w);
add_edge(&G, u, v, }
w); pi[s] = 0;
}
#include <stdio.h> p[s] = -1;
#define MAX_VERTICES 50 for (it = 1; it < G->n; it++) {
#define MAX_EDGES 50 int min_pi = INFINITY;
#define NO_EDGE -1 for (j = 1; j <= G->n; j++)

typedef struct { if (mark[j] == 0 && pi[j] < min_pi) {


int n, m; min_pi = pi[j];

int A[MAX_VERTICES][MAX_EDGES]; i = j;

} Graph; }

void init_graph(Graph* G, int n, int m) { mark[i] = 1;

int i, j; for (j = 1; j <= G->n; j++)

G->n = n; if (G->A[i][j] != NO_EDGE && mark[j] == 0) {


G->m = m; if (pi[i] + G->A[i][j] < pi[j]) {
for (i = 1; i <= n; i++) pi[j] = pi[i] + G->A[i][j];
for (j = 1; j <= m; j++) p[j] = i;
G->A[i][j] = NO_EDGE; }

} }
void add_edge(Graph* G, int x, int y, int w) { }
G->A[x][y] = w; }
G->A[y][x] = w; int main() {
} // freopen("dt.txt", "r", stdin);
#define INFINITY 9999999 Graph G;
22
int n, m, u, v, e, w;  Thêm dòng freopen("dt.txt", "r", stdin); vào
scanf("%d%d", &n, &m); ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
dòng này ra.
init_graph(&G, n, m);  Có thể sử dụng đoạn chương
trình đọc dữ liệu mẫu sau đây:
for (e = 1; e <= m; e++) {
freopen("dt.txt", "r", stdin);
scanf("%d%d%d", &u, &v, &w);
//Khi nộp bài nhớ bỏ dòng này.
add_edge(&G,u,v,w); Graph G;
int n, m, u, v, w, e;
} scanf("%d%d", &n, &m);
init_graph(&G, n);
Dijkstra(&G, 1);
if(pi[n]>0){ for (e = 0; e < m; e++) {

printf("%d", pi[n]); scanf("%d%d%d", &u, &v,


} &c);
add_edge(&G, u, v,
else printf("-1"); w);
}
return 0;
} #include <stdio.h>
2. TÌM ĐƯỜNG ĐI NGẮN NHẤT #define MAX_VERTICES 50
(CHECK ĐƯỢC)
#define MAX_EDGES 50
Cho đồ thị có hướng G = <V, E> có n đỉnh và m cung
(n < 100, m < 500). Mỗi cung được gán một trọng số #define NO_EDGE -1
w (0 < w <= 100).
typedef struct {
Viết chương trình tìm đường đi ngắn nhất từ đỉnh 1
đến n. int n, m;
Đầu vào (Input): int A[MAX_VERTICES][MAX_EDGES];
Dữ liệu đầu vào được nhập từ bàn phím với định } Graph;
dạng:
- Dòng đầu tiên chứa 2 số nguyên n và m. void init_graph(Graph* G, int n, int m) {

- m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v, int i, j;


w mô tả cung (u, v) có trọng số w.
G->n = n;
Đầu ra (Output):
G->m = m;
In ra màn hình chiều dài của đường đi ngắn nhất từ
1 đến n. Nếu không có đường đi từ 1 đến n, in ra -1. for (i = 1; i <= n; i++)
Xem thêm ví dụ bên dưới. for (j = 1; j <= m; j++)
Chú ý:
G->A[i][j] = NO_EDGE;
 Để chạy thử chương trình, bạn nên tạo một }
tập tin dt.txt chứa đồ thị cần kiểm tra.
void add_edge(Graph* G, int x, int y, int w) {
G->A[x][y] = w;
G->A[y][x] = w;
23
} Graph G;
#define INFINITY 9999999 int n, m, u, v, e, w;
int mark[MAX_VERTICES]; scanf("%d%d", &n, &m);
int pi[MAX_VERTICES]; init_graph(&G, n, m);
int p[MAX_VERTICES]; for (e = 1; e <= m; e++) {
void Dijkstra(Graph* G, int s) { scanf("%d%d%d", &u, &v, &w);
int i, j, it; add_edge(&G,u,v,w);
for (i = 1; i <= G->n; i++) { }
pi[i] = INFINITY; Dijkstra(&G, 1);
mark[i] = 0; if(pi[n]>0){
} printf("%d", pi[n]);
pi[s] = 0; }
p[s] = -1; else printf("-1");
for (it = 1; it < G->n; it++) { return 0;
int min_pi = INFINITY; }
for (j = 1; j <= G->n; j++)
if (mark[j] == 0 && pi[j] < min_pi) { Cho đồ thị vô hướng G = <V, E> có n đỉnh và m cung
min_pi = pi[j]; (n < 100, m < 500). Mỗi cung được gán một trọng số
w (0 < w <= 100).
i = j; Viết chương trình tìm đường đi ngắn nhất từ đỉnh 1
} đến n.
Đầu vào (Input):
mark[i] = 1;
Dữ liệu đầu vào được nhập từ bàn phím với định
for (j = 1; j <= G->n; j++) dạng:
if (G->A[i][j] != NO_EDGE && mark[j] == 0) { - Dòng đầu tiên chứa 2 số nguyên n và m.

if (pi[i] + G->A[i][j] < pi[j]) { - m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v,
w mô tả cung (u, v) có trọng số w.
pi[j] = pi[i] + G->A[i][j]; Đầu ra (Output):
p[j] = i; In ra màn hình chiều dài của đường đi ngắn nhất từ
1 đến n. Nếu không có đường đi từ 1 đến n, in ra -1.
}
Xem thêm ví dụ bên dưới.
}
Chú ý:
}
 Để chạy thử chương trình, bạn nên tạo một
} tập tin dt.txt chứa đồ thị cần kiểm tra.
int main() {  Thêm dòng freopen("dt.txt", "r", stdin); vào
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
// freopen("dt.txt", "r", stdin); dòng này ra.

24
 Có thể sử dụng đoạn chương trình đọc dữ int mark[MAX_VERTICES];
liệu mẫu sau đây:
int pi[MAX_VERTICES];
freopen("dt.txt", "r", stdin);
//Khi nộp bài nhớ bỏ dòng này. int p[MAX_VERTICES];
Graph G; void Dijkstra(Graph* G, int s) {
int n, m, u, v, w, e;
scanf("%d%d", &n, &m); int i, j, it;
init_graph(&G, n);
for (i = 1; i <= G->n; i++) {
for (e = 0; e < m; e++) {
pi[i] = INFINITY;
scanf("%d%d%d",
&u, &v, &w); mark[i] = 0;
add_edge(&G, u, v,
w); }
}
pi[s] = 0;
p[s] = -1;
#include <stdio.h>
for (it = 1; it < G->n; it++) {
#define MAX_VERTICES 50
int min_pi = INFINITY;
#define MAX_EDGES 50
for (j = 1; j <= G->n; j++)
#define NO_EDGE -1
if (mark[j] == 0 && pi[j] < min_pi) {
typedef struct {
min_pi = pi[j];
int n, m;
i = j;
int A[MAX_VERTICES][MAX_EDGES];
}
} Graph;
mark[i] = 1;
void init_graph(Graph* G, int n, int m) {
for (j = 1; j <= G->n; j++)
int i, j;
if (G->A[i][j] != NO_EDGE && mark[j] == 0) {
G->n = n;
if (pi[i] + G->A[i][j] < pi[j]) {
G->m = m;
pi[j] = pi[i] + G->A[i][j];
for (i = 1; i <= n; i++)
p[j] = i;
for (j = 1; j <= m; j++)
}
G->A[i][j] = NO_EDGE;
}
}
}
void add_edge(Graph* G, int x, int y, int w) {
}
G->A[x][y] = w;
int main() {
G->A[y][x] = w;
// freopen("dt.txt", "r", stdin);
}
Graph G;
#define INFINITY 9999999
int n, m, u, v, e, w;
25
scanf("%d%d", &n, &m);  Thêm dòng freopen("dt.txt", "r", stdin); vào
init_graph(&G, n, m); ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
dòng này ra.
for (e = 1; e <= m; e++) {  Có thể sử dụng đoạn chương trình đọc dữ
liệu mẫu sau đây:
scanf("%d%d%d", &u, &v, &w);
add_edge(&G,u,v,w); freopen("dt.txt", "r",
stdin); //Khi nộp bài nhớ bỏ dòng
} này.
Graph G;
Dijkstra(&G, 1); int n, m, u, v, w, e;
scanf("%d%d", &n, &m);
if(pi[n]>0){
init_graph(&G, n);
printf("%d", pi[n]);
for (e = 0; e < m; e++) {
}
scanf("%d%d%d", &u,
else printf("-1"); &v, &w);
return 0; add_edge(&G, u,
v, w);
} }

#include <stdio.h>
3. KIỂM TRA CHU TRÌNH ÂM VÀ
#define MAXN 1000
ỨNG DỤNG TÌM ĐƯỜNG ĐI NGẮN
NHẤT #define INFINITY 9999999
Viết chương trình kiểm tra một đồ thị có hướng int pi[MAXN];
(không có khuyên, không có đa cung) xem có chứa
chu trình âm hay không. int p[MAXN];
Chu trình âm là chu trình có tổng trọng số các cung typedef struct {
trong chu trình nhỏ hơn 0.
int u, v;
Đầu vào (Input):
int w;
Dữ liệu đầu vào được nhập từ bàn phím với định
dạng: } Edge;
- Dòng đầu tiên chứa 2 số nguyên n và m, tương
typedef struct {
ứng là số đỉnh và số cung.
- m dòng tiếp theo mỗi dòng chứa 2 số nguyên u, v, int n, m;
w mô tả cung (u, v) có trọng số w. Edge edges[MAXN];
Đầu ra (Output):
} Graph;
In ra màn hình negative cycle nếu đồ thị có chứa chu
trình âm, ngược lại in ra ok void init_graph(Graph *G, int n, int m){
Xem thêm ví dụ bên dưới. G->n = n;
Chú ý: G->m = 0;
 Để chạy thử chương trình, bạn nên tạo một }
tập tin dt.txt chứa đồ thị cần kiểm tra.
void add_edge(Graph *G, int u, int v, int w){

26
G->edges[G->m].u = u;

27
G->edges[G->m].v = v; }
G->edges[G->m].w = w; }
G->m++;
int main(){
}
// freopen("dt.txt", "r", stdin);
void BellmanFord(Graph *G, int s){
Graph G;
int i, it,k;
int n, m, u, v, e, w;
for(i=1; i<= G->n; i++){
scanf("%d%d", &n, &m);
pi[i] = INFINITY;
init_graph(&G, n, m);
}
for (e = 1; e <= m; e++) {
pi[s] = 0;
scanf("%d%d%d", &u, &v, &w);
p[s] = -1;
add_edge(&G,u,v,w);
for(it = 1; it < G->n; it++){
}
for(k=0; k< G->m; k++){
BellmanFord(&G, 1);
int u = G->edges[k].u;
/* for(i=1; i<=G.n;i++)
int v = G->edges[k].v;
printf("pi[%d] = %d, p[%d] = %d\n", i, pi[i], i, p[i]);*/
int w = G->edges[k].w;
return 0;
if(pi[u] + w < pi[v]){
}
pi[v] = pi[u] + w;
p[v] = u;
…..
}
}
} 4. ỨNG DỤNG TÌM ĐƯỜNG ĐI NGẮN
for(k=0; k< G->m; k++){ NHẤT
int u = G->edges[k].u;
int v = G->edges[k].v; Đất nước CyberGraph có n thành phố và m con
đường. Mỗi con đường nối 2 thành phố lại với nhau.
int w = G->edges[k].w; tất cả các con đường đều là đường 2 chiều, mỗi con
if(pi[u] + w < pi[v]){ đường có một chiều dài nào đó. giữa hai thành phố có
nhiều nhất là 1 con đường.
printf("negative cycle"); break;
Tổng thổng của nước này dự định sẽ đi từ thành
} phố s đến thành phố t. Đương nhiên, ông ta sẽ chọn
hành trình có tổng chiều dài các con đường đi qua
else { ngắn nhất.
printf("ok"); break; Hãy giúp Ngài tổng thổng tìm hành trình ngắn nhất
đi từ s đến t.
}
Đầu vào (Input):
28
Dữ liệu đầu vào được nhập từ bàn phím G->A[y][x]=w;
với định dạng:
}
- Dòng đầu tiên chứa 2 số nguyên n và m,
tương ứng là số thành phố và số con int degree(Graph* G,int x){
đường.
int y,deg=0;
- m dòng tiếp theo mỗi dòng chứa 3 số
nguyên u v d mô tả con đường nối hai for(y=1;y<=G->n;y++)
thành phố u và v có chiều dài d.
if(G->A[x][y]>0)
- Dòng cuối cùng chứa số nguyên s t là
thành phố bắt đầu và kết thúc. deg+=G->A[x][y];
Đầu ra (Output): return deg;
In ra màn hình tổng chiều dài các con
đường của hành trình ngắn nhất.. }

Xem thêm ví dụ bên dưới. int adjacent(Graph* G,int x,int y){


if(G->A[x][y]!=0)
//Ung dung tim duong di ngan nhat return 1;
#include <stdio.h> else
#define MAX 100 return 0;
#define NO_EDGE 9999999 }
#define INFINITY 999999 void Dijkstra(Graph *G,int s){
int mark[MAX]; int i,j,it;
int pi[MAX]; for(i=1;i<=G->n;i++){
int p[MAX]; pi[i]=INFINITY;
typedef struct{ mark[i]=0;
int n; }
int A[MAX][MAX]; pi[s]=0;
}Graph; p[s]=-1;
void init_graph(Graph* G,int n){ for(it=1;it<G->n;it++){
int i,j; int min_pi
G->n=n; =INFINITY;
for(i=1;i<=n;i++) for(j=1;j<=G->n;j++){
for(j=1;j<=n;j++) if(mark[j]==0&&pi[j]<min_pi){
G->A[i][j]=NO_EDGE; min_pi= pi[j];
} i=j;
void add_edge(Graph* G,int x,int y,int w){ }
G->A[x][y]=w; }
29
mark[i]=1;

30
for(j=1;j<=G->n;j++){
if(G->A[i][j] != NO_EDGE && Một con robot
mark[j]==0){ được đặt tại góc
trên bên trái của
if(pi[i]+G->A[i][j]<pi[j]){ mê cung và muốn
đi đến góc dưới
pi[j]=pi[i]+G->A[i][j];
bên phải của mê
p[j]=i; cung. Con robot có thể đi lên, xuống, qua trái và qua
phải 1 ô. Chi phí để đi đến một ô bằng với con số bên
} trong ô đó.
} Hãy tìm cách giúp con robot đi đến ô góc dưới
phải sao cho tổng chi phí thấp nhất.
}
Đường đi có chi phí thấp nhất cho ví dụ này là 24.
}
} Đầu vào (Input):

int main(){ Dữ liệu đầu vào được nhập từ bàn phím với định
// freopen("ungdung.txt", "r", stdin); dạng:

Graph G;  Dòng dầu chứa 2 số nguyên M N (M: số hàng,


N: số cột)
int n, m, u, v, w, e, s,t;  M dòng tiếp theo mô tả các số
scanf("%d%d", &n, &m); trong mê cung

init_graph(&G, n);
for (e = 0; e < m; e++) { scanf("%d
%d%d", &u, &v, &w); Đầu ra (Output):
In ra màn hình chi phí thấp nhất để
add_edge(&G, u, v , w);
con robot đi từ góc trên bên trái về
} góc dưới bên phải. Ví dụ trên, cần in
ra màn hình: 24.
scanf("%d%d", &s, &t); Xem thêm các ví dụ bên dưới.
Dijkstra(&G, s); Gợi ý:

printf("%d", pi[t]); Mô hình hoá bài toán về đồ thị có hướng

return 0;  Đỉnh ~ ô
 Cung ~ hai ô cạnh nhau
}  Trọng số cung (u, v) = giá trị của ô tương ứng
với đỉnh v.
5. MÊ CUNG SỐ
Mê cung số (number maze) Xem tài liệu thực hành để biết cách đặt tên cho các ô.
Cho một mê cung số được biểu diễn bằng một
mảng 2 chiều chứa các con số từ 0 đến 9 (xem #include<stdio.h>
hình bên dưới).
#define MAX 100
#define INFINITY 999999
#define NO_EDGE 0
31
int w[MAX]; >n;j++)
typedef struct{
int A[MAX][MAX];
int n,m;
}Graph;
void init_graph(Graph *G, int n){
int i,j;
G->n=n;
for(i=1;i<=G->n;i++)
for(j=1;j<=G->n;j++)
G->A[i][j] = 0;
}
void add_edge(Graph *G, int u,int v , int w){
G->A[u][v] = w;
G->A[v][u] = w;
}
int mark[MAX];
int pi[MAX];
int p[MAX];
void Dijkstra(Graph *G, int s){
int u,v,it;
for(u=1;u<=G->n;u++){
mark[u] = 0;
pi[u] = INFINITY;
}
pi[s] = w[s];
p[s] = -1;
for(it=1;it<= G->n;it++){
// 1. Tim j co mark[j] == 0 va co pi[j] la nho nhat va
gan u = j;
int j , min_pi =
INFINITY; for(j=1;j<=G-

32
if(mark[j] == 0 && pi[j] < for(i=0;i<m;i++)
min_pi){ min_pi = pi[j]; for(j=0;j<n;j++){
u = j; scanf("%d ",&u); // gan gia tri cua i , j cho
} u w[i*n+j+1]=u; // u chuyen qua w[u]
// Danh dau mark[u] da }
xet mark[u] = 1; // for(i=0;i < m;i++){
// Cap nhat lai pi va p cua cac dinh ke cua u // for(j=0;j<n;j++){
( neu thoa)
// printf("%d ",w[i*n+j+1]);
for(v=1;v<=G->n;v++)
if(G->A[u][v] != NO_EDGE && mark[v] == 0){
if(pi[u] + G->A[u][v] < pi[v]){
pi[v] = pi[u] + G->A[u]
[v]; p[v] = u;
}
}
}
}
int
main(
){
Grap
h G;
int
m,n,u,v,i,j;
int
k,i_ke,j_ke
;
//freopen("mecungso3.txt","r",stdin);
scanf("%d %d",&m,&n); // Nhap M : so hang , N :
so
cot
init_graph(&G,n*
m);

33
// } Viết chương trình tìm
// printf("\n"); đường đi ngắn nhất từ
đỉnh 1 đến các đỉnh còn
// } lại.

for(i=0;i<m;i++) Đầu vào (Input):


Dữ liệu đầu vào được
for(j=0;j<n;j++){ nhập từ bàn phím với
int di[] = {-1,1,0,0}; định dạng:

int dj[] = {0,0,-1,1};  Dòng đầu tiên


chứa 2 số nguyên
// Duyet qua cac o ke cua o (i,j) n và m.
for(k=0;k<4;k++){  m dòng tiếp theo
mỗi dòng chứa 3
i_ke = i+di[k]; số nguyên u, v, w
mô tả cung (u, v)
j_ke = j + dj[k]; có trọng số w.
// Kt o (i_ke,j_ke) co nam trong me cung ko?
Dữ liệu được đảm bảo
if((i_ke >=0) && (i_ke < m) && (j_ke >=0) không tồn tại chu trình âm.
&& (j_ke < n)){ Đầu ra (Output):
v = i_ke * n + j_ke +1; In ra màn hình các giá trị pi và p của các đỉnh theo
thứ tự 1, 2, ..., n.
u = i * n + j +1;
Xem thêm ví dụ bên dưới.
G.A[u][v] = w[v];
Chú ý:
}
 Để chạy thử chương trình, bạn nên tạo một
} tập tin dt.txt chứa đồ thị cần kiểm tra.
}  Thêm dòng freopen("dt.txt", "r", stdin); vào
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
Dijkstra(&G,1); dòng này ra.
 Có thể sử dụng đoạn chương trình đọc dữ
printf("%d ",pi[G.n]); liệu mẫu sau đây:
return 0;
freopen("dt.txt", "r", stdin); //Khi nộp bài
} nhớ bỏ dòng này.
Graph G;
int n, m, u, v, w, e;
scanf("%d%d", &n, &m);
6. BELLMAN – FORD PI VÀ PI
init_graph(&G, n);
Cho đồ thị có hướng G = <V, E> có n đỉnh và m cung
(n < 100, m < 500). Mỗi cung được gán một trọng số for (e = 0; e < m; e++) {
w (-100 < w <= 100). scanf("%d%d%d", &u, &v, &c);
add_edge(&G, u, v, w);
}

#include <stdio.h>
#define MAXN 1000
#define INFINITY 9999999
34
int pi[MAXN]; if(pi[u] + w < pi[v]){
int p[MAXN]; pi[v] = pi[u] + w;
typedef struct { p[v] = u;
int u, v; }
int w; }
} Edge; }
typedef struct { }
int n, m; int main(){
Edge edges[MAXN]; // freopen("dt.txt", "r", stdin);
} Graph; Graph G;
void init_graph(Graph *G, int n, int m){ int n, m, u, v, e, w;
G->n = n; int i;
G->m = 0; scanf("%d%d", &n, &m);
} init_graph(&G, n, m);
void add_edge(Graph *G, int u, int v, int w){ for (e = 1; e <= m; e++) {
G->edges[G->m].u = u; scanf("%d%d%d", &u, &v, &w);
G->edges[G->m].v = v; add_edge(&G,u,v,w);
G->edges[G->m].w = w; }
G->m++; BellmanFord(&G, 1);
} for(i=1; i<=G.n;i++)
void BellmanFord(Graph *G, int s){ printf("pi[%d] = %d, p[%d] = %d\n", i, pi[i], i,
int i, it,k; p[i]);

for(i=1; i<= G->n; i++){ }

pi[i] = INFINITY;
} 7. BELLMAN FORD

pi[s] = 0; Áp dụng giải thuật Bellman – Ford kiểm tra xem một
đồ thị có hướng có chứa chu trình âm hay không, nếu
p[s] = -1; ta tìm đường đi ngắn nhất từ đỉnh s đến các đỉnh còn
lại. In kết quả YES (nếu đồ thị có chu trình âm) hoặc
for(it = 1; it < G->n; it++){ NO (trường hợp ngược lại).
for(k=0; k< G->m; k++){ Đầu vào (Input):
int u = G->edges[k].u; Dữ liệu đầu vào được nhập từ bàn phím với định
dạng:
int v = G->edges[k].v;
- Dòng đầu tiên chứa 2 số nguyên n và m tương ứng
int w = G->edges[k].w; là số đỉnh và số cung.

35
- m dòng tiếp theo mỗi dòng chứa 3 typedef struct {
số nguyên u, v, w nói rằng cung (u,
v) có trọng số w. int n, m;
- Dòng cuối cùng chứa đỉnh Edge edges[MAXN];
s. Đầu ra (Output):
} Graph;
In ra màn hình YES nếu phát hiện
chu trình âm, ngược lại in ra NO. void init_graph(Graph *G, int n, int m){
Xem thêm ví dụ bên dưới. G->n = n;
Chú ý: G->m = 0;

 Để chạy thử chương trình, }


bạn nên tạo một tập
tin dt.txt chứa đồ thị cần void add_edge(Graph *G, int u, int v, int w){
kiểm tra. G->edges[G->m].u = u;
 Thêm dòng
freopen("dt.txt", "r", G->edges[G->m].v = v;
stdin); vào ngay sau hàm
main(). Khi nộp bài, nhớ gỡ G->edges[G->m].w = w;
bỏ dòng này ra. G->m++;
 Có thể sử dụng đoạn
chương trình đọc dữ liệu }
mẫu sau đây:
void BellmanFord(Graph *G, int s){
freopen("dt.txt", "r", int i, it,k;
stdin); //Khi nộp bài nhớ bỏ dòng này.
Graph G; for(i=1; i<= G->n; i++){
int n, m, u, v, w, e;
scanf("%d%d", &n, &m); pi[i] = INFINITY;
init_graph(&G, n);
}
for (e = 0; e < m; e++) { pi[s] = 0;
scanf("%d%d%d", &u, &v,
&w); p[s] = -1;
add_edge(&G, u, v, w); for(it = 1; it < G->n; it++){
}
for(k=0; k< G->m; k++){
#include <stdio.h> int u = G->edges[k].u;
#define MAXN 1000 int v = G->edges[k].v;
#define INFINITY 9999999 int w = G->edges[k].w;
int pi[MAXN]; if(pi[u] + w < pi[v]){
int p[MAXN]; pi[v] = pi[u] + w;
typedef struct { p[v] = u;
int u, v; }
int w; }
} Edge; }

36
for(k=0; k< G->m; k++){ Cho đồ thị có hướng G = <V, E> có n đỉnh và m cung
int u = G->edges[k].u; (n < 100, m < 500). Mỗi cung được gán một trọng số
w (-100 < w <= 100).
int v = G->edges[k].v; Áp dụng giải thuật Floyd - Warshall viết chương
int w = G->edges[k].w; trình tìm đường đi ngắn nhất giữa các cặp đỉnh. In
chiều dài ngắn nhất giữa các cặp đỉnh ra màn hình
if(pi[u] + w < pi[v]){ theo dạng:

pi[v] = pi[u] + w; x -> y: chiều dài


...
p[v] = u;
}
Đầu vào (Input):
}
Dữ liệu đầu vào được nhập từ bàn phím với định
} dạng:

int main(){  Dòng đầu tiên chứa 2 số nguyên n và m.


 m dòng tiếp theo mỗi dòng chứa 3 số nguyên
// freopen("dt.txt", "r", stdin);
u, v, w mô tả cung (u, v) có trọng số w.
Graph G;
Dữ liệu được đảm bảo không tồn tại chu trình âm.
int n, m, u, v, e, w;
Đầu ra (Output):
scanf("%d%d", &n, &m); In ra màn hình chiều dài đường đi ngắn nhất giữa
init_graph(&G, n, m); các cặp đỉnh. Nếu không có đường đi in ra oo (vô
cùng).
for (e = 1; e <= m; e++) { Liệt kê các cặp theo thứ tự tăng dần của x, và y.
scanf("%d%d%d", &u, &v, &w); Xem thêm ví dụ bên dưới.
add_edge(&G,u,v,w); Chú ý:

}  Để chạy thử chương trình, bạn nên tạo một


BellmanFord(&G, 1); tập tin dt.txt chứa đồ thị cần kiểm tra.
 Thêm dòng freopen("dt.txt", "r", stdin); vào
if (pi[n] > 0) ngay sau hàm main().
Khi nộp bài, nhớ gỡ
printf ("NO"); bỏ dòng này ra.
 Có thể sử dụng đoạn
else
chương trình đọc dữ
printf ("YES"); liệu mẫu sau đây:

return 0; freopen("dt.txt",
"r", stdin); //Khi nộp bài nhớ
// printf("%d", pi[4]);
bỏ dòng này.
/* for(i=1; i<=G.n;i++) Graph G;
int n, m, u, v, w, e;
printf("pi[%d] = %d, p[%d] = %d\n", i, pi[i], i, p[i]);*/ scanf("%d%d",
&n, &m);
} init_graph(&G, n);

for (e = 0; e < m; e++) {


8. FLOYD – WARSALL scanf("%d%d%d", &u, &v, &c);

37
add_edge(&G, u, v, w); for(u=1; u<=G->n; u++)
}
for(v=1; v<=G->n; v++)

#include<stdio.h> if(G->L[u][v] != NO_EDGE){

#define MAXN 500 pi[u][v] = G->L[u][v];

#define NO_EDGE 951 next[u][v] = v;


typedef struct{
}
int n;
for(k=1; k<=G->n; k++)
int L[MAXN][MAXN];
for(u=1; u<=G->n; u++)
}Graph; for(v=1; v<=G->n; v++)
void init_graph(Graph* G, int n){ if(pi[u][k] + pi[k][v] < pi[u][v]){
G->n=n; pi[u][v] = pi[u][k] + pi[k][v];
int i, j;
next[u][v] = next[u][k];
for(i=1; i<=n; i++) }
for(j=1; j<=n; j++) }
G->L[i][j] = NO_EDGE;
int main(){
}
Graph G;
void add_edge(Graph* G, int u, int v, int w){
int n, m, u, v, w, e;
G->L[u][v]=w;
// freopen("floy1.txt", "r", stdin);
}
scanf("%d%d",&n,&m);
// giai thuat init_graph(&G,n);
#define INFINITY 9000 for(e=1; e<=m; e++){ scanf("%d%d
int pi[MAXN][MAXN]; %d",&u,&v,&w);
int next[MAXN][MAXN]; add_edge(&G,u,v,w);
void Floyd_Warshall(Graph* G){ G.L[u][v]=w;
int u, v, k; }
for(u=1; u<=G->n; u++) Floyd_Warshall(&G);
for(v=1; v<=G->n; v++){
for(u=1; u<=G.n; u++)
pi[u][v] = INFINITY;
for(v=1; v<=G.n; v++)
next[u][v] = -1;
if(pi[u][v]>=INFINITY-5){
} printf("%d -> %d: oo\n",u,v);
for(u=1; u<=G->n; u++) }else
pi[u][u] = 0; printf("%d -> %d: %d \n",u,v,pi[u][v]);
38
return 0; #define MAX_ELEMENTS 100
} typedef int ElementType;
int rank[MAX_VERTICES];

1. XẾP HẠNG ĐỒ THỊ int d[MAX_VERTICES];

Viết chương trình xếp hạng cho đồ thị có hướng //khai bao cau truc list
không chu trình.
typedef struct {
Đầu vào (Input):
ElementType data[MAX_ELEMENTS];
Dữ liệu đầu vào được nhập từ bàn phím với định
dạng: int size;
- Dòng đầu tiên chứa 2 số nguyên n và m, tương ứng } List;
là số đỉnh và số cung.
- m dòng tiếp theo mỗi dòng chứa 2 số nguyên u, v //khoi bao cau truc do thi
mô tả cung (u, v). typedef struct {
Đầu ra (Output):
int A[MAX_VERTICES][MAX_VERTICES];
In ra màn hình hạng của các đỉnh theo thứ tự của
đỉnh, mỗi đỉnh trên 1 dòng: int n,m;
Hạng đỉnh 1 }Graph;
Hạng đỉnh 2 void make_null_list(List* L) {
...
L->size = 0;
Hạng đỉnh n
}
Xem thêm ví dụ bên dưới. Trong ví dụ đầu tiên ta có:
hạng của 1 = 0, hạng của 2 = 2 và hạng của 3 = 1. /* Them mot phan tu vao cuoi danh sach */
Chú ý: void push_back(List* L, ElementType x) {

 Để chạy thử chương trình, bạn nên tạo một L->data[L->size] = x;


tập tin dt.txt chứa đồ thị cần kiểm tra.
 Thêm dòng freopen("dt.txt", "r", stdin); vào L->size++;
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ }
dòng này ra.
 Có thể sử dụng đoạn chương trình đọc dữ /* Lay phan tu tai vi tri i, phan tu bat
liệu mẫu sau đây: dau o vi tri 1 */

freopen("dt.txt", "r", stdin); //Khi nộp bài ElementType element_at(List* L, int i)


nhớ bỏ dòng này. {
Graph G;
return L->data[i-1];
int n, m, u, v, w, e;
scanf("%d%d", &n, &m); }
init_graph(&G, n);
/* Tra ve so phan tu cua danh sach */
for (e = 0; e < m; e++) {
scanf("%d%d%d", &u, &v); int count_list(List* L) {
add_edge(&G, u, v); return L->size;
}
#include <stdio.h> }
#define MAX_VERTICES 100
39
/* phan do thi */

40
void init_graph(Graph *G,int n){ for(i=1;i<=S2->size;i++){
int i,j; x=element_at(S2,i);
G->n = n; push_back(S1,x);
for(i=1;i<=n;i++) }
for(j=1;j<=n;j++) }
G->A[i][j]=0; int k=0;
} List S1, S2;
void add_edge(Graph *G, int x,int y){ void ranking(Graph *G){
G->A[x][y]=1; int x, u;
} for(u = 1; u <= G->n; u++){
int adjacent(Graph *G, int x, int y){ d[u] = 0;
return G->A[x][y] != 0; // rank[u] = 0;
} }
for(x = 1; x <= G->n; x++)
int degree(Graph *G,int x){ for(u = 1; u <= G->n; u++)
int y,deg=0; if(G->A[x][u] != 0)
for(y=1; y<= G->n; y++) d[u]++;
deg+= G->A[x][y]; // d[1]=0;
return deg; // List S1, S2;
} make_null_list(&S1);
List neighbors(Graph *G, int x){ for(u = 1; u <= G->n; u++)
int y; if(d[u] == 0)
List list; push_back(&S1, u);
make_null_list(&list); // int k = 1, i;
for(y=1;y<=G->n;y++) int i;
if(adjacent(G,x,y)) while(S1.size > 0){
push_back(&list,y); make_null_list(&S2);
return list; for(i = 1; i <= S1.size; i++){
} int u = element_at(&S1, i);
void copy_list(List *S1, List *S2){ rank[u] = k;
int i, x; int v;
make_null_list(S1); for (v = 1; v <= G->n; v++)
41
if(G->A[u][v] != 0){ nếu đi từ trong nhà ra cổng, ta sẽ gặp các hòn đá
d[v]--; có khối lượng tăng dần.
Tuy nhiên, điều khó khăn đối với Peter là anh chỉ
if(d[v] == 0) có một cây cân đĩa mà không
push_back(&S2, v); có quả cân nào. Nói cách
khác, mỗi lần cân Peter chỉ có
} thể biết được hòn đá nào nhẹ
hơn hòn đá nào chứ không
} biết nó nặng bao nhiêu kg.
copy_list(&S1, &S2); Sau m lần cân, Peter biết
được sự khác nhau về cân
k++; nặng của m cặp. Với các
} thông tin này, hãy giúp Peter
sắp xếp các viên đá theo thứ
} tự anh mong muốn.

int main (){ Đầu vào (Input):


Dữ liệu đầu vào được nhập từ
// freopen("dt.txt", "r", stdin);
bàn phím với định dạng:
Graph G; - Dòng đầu tiên chứa 2 số
nguyên n và m, tương ứng là số hòn đó và số lần
int n, m, u, v, e;
cân
scanf("%d%d", &n, &m); - m dòng tiếp theo mỗi dòng chứa 2 số nguyên u v
init_graph(&G, n); nói rằng hòn đá u nhẹ hơn hòn đá v.
Đầu ra (Output):
for (e = 0; e < m; e++) {
In ra màn hình thứ tự của các hòn đá theo khối
scanf("%d%d", &u, &v); lượng tăng dần. In các số thứ tự trên cùng một
dòng, mỗi số cách nhau một khoảng trắng.
add_edge(&G, u, v);
Bạn có thể yên tâm là dữ liệu đầu được giả sử
} rằng chỉ có một kết quả quả duy nhất.
ranking(&G); Xem thêm ví dụ bên dưới. Trong ví dụ đầu tiên ta
có: hòn đá 1 nhẹ nhất, kế đến là hòn đá 3 và sau
for(u=1;u<=n;u++) cùng là hòn đá 2.
printf("%d \n",rank[u]);
return 0; #include <stdio.h>
} #define MAX_VERTICES 100
#define MAX_ELEMENTS 100
2. CÂN ĐÁ typedef int ElementType;
Peter rất thích chơi đá. Anh ta thường dùng đá int rank[MAX_VERTICES];
để trang trí sân nhà của mình. Hiện tại Peter có n
hòn đá. Dĩ nhiên mỗi hòn đá có một khối lượng //khai bao cau truc list
nào đó. Peter muốn đặt các hòn đá này dọc theo typedef struct {
lối đi từ cổng vào nhà của mình. Peter lại muốn
sắp xếp như thế này: hòn đá nặng nhất sẽ đặt ở ElementType data[MAX_ELEMENTS];
cạnh cổng rào, kế tiếp là hòn đá nặng thứ 2, ...
hòn đá nhẹ nhất sẽ được đặt cạnh nhà. Như vậy int size;

42
} List; Q->rear++;
typedef struct { Q->data[Q->rear] = x;
int data[MAX_ELEMENTS]; }
int front, rear; int top(Queue* Q) {
} Queue; return Q->data[Q->front];
//khoi bao cau truc di thi }
typedef struct { void pop(Queue* Q) {
int A[MAX_VERTICES][MAX_VERTICES]; Q->front++;
int n,m; }
} Graph; int empty(Queue* Q) {
void make_null_list(List* L) { return Q->front > Q->rear;
L->size = 0; }
} /* phan do thi */
void init_graph(Graph *G,int n){
/* Them mot phan tu vao cuoi danh sach */ int i,j;
void push_back(List* L, ElementType x) { G->n = n;
L->data[L->size] = x; for(i=1;i<=n;i++)
L->size++; for(j=1;j<=n;j++)
} G->A[i][j]=0;
/* Lay phan tu tai vi tri i, phan tu bat dau o vi tri 1 }
*/ ElementType element_at(List* L, int i) { void add_edge(Graph *G, int x,int y){
return L->data[i-1]; G->A[x][y]=1;
} }
/* Tra ve so phan tu cua danh sach */ int adjacent(Graph *G, int x, int y){
int count_list(List* L) { return G->A[x][y] != 0;
return L->size; }
} int degree(Graph *G,int x){
void make_null_queue(Queue *Q) { int y,deg=0;
Q->front = 0; for(y=1; y<= G->n; y++)
Q->rear = -1; deg+= G->A[x][y];
} return deg;
void push(Queue* Q, int x) { }
43
List neighbors(Graph *G, int x){ if(d[u] == 0)
int y; push(&Q, u);
List list; List L;
make_null_list(&list); make_null_list(&L);
for(y=1;y<=G->n;y++) while(!empty(&Q)){
if(adjacent(G,x,y)) int u = top(&Q); pop(&Q);
push_back(&list,y); push_back(&L, u);
return list; int v;
} for (v = 1; v <= G->n; v++)
if(G->A[u][v] != 0){
void copy_list(List *S1, List *S2){ d[v]--;
int i, x; if(d[v] == 0)
make_null_list(S1); push(&Q, v);
for(i=1;i<=S2->size;i++){ }
x=element_at(S2,i); }
push_back(S1,x); return L;
} }
} int main (){
List topo_sort(Graph *G){ // freopen("dt.txt", "r", stdin);
int d[MAX_VERTICES]; Graph G;
int x, u; int n, m, u, v, e;
for(u = 1; u <= G->n; u++){ scanf("%d%d", &n, &m);
d[u] = 0; init_graph(&G, n);
// rank[u] = 0; for (e =1 ; e <= m; e++) {
} scanf("%d%d", &u, &v);
for(x = 1; x <= G->n; x++) add_edge(&G, u, v);
for(u = 1; u <= G->n; u++) }
if(G->A[x][u] != 0) List L = topo_sort(&G);
d[u]++; for(u=1;u<=L.size;u++)
Queue Q; printf("%d ",element_at(&L,u));
make_null_queue(&Q); return 0;
for(u = 1; u <= G->n; u++) }
44
3. CHIA KẸO int size;
Cô giáo Trang chuẩn bị kẹo để phát cho các bé } List;
mà cô đang giữ. Dĩ nhiên môi bé đều có một tên
gọi rất dễ thương ví dụ: Mạnh Phát, Diễm //khoi bao cau truc do thi
Quỳnh, Đăng Khoa, ... Tuy nhiên, để đơn giản
vấn đề ta có thể giả sử các em được đánh số từ 1 typedef struct {
đến n.
int A[MAX_VERTICES][MAX_VERTICES];
Cô giáo muốn rằng tất cả các em đều phải có kẹo.
Cô lại biết thêm rằng có một số bé có ý muốn hơn int n,m;
bạn mình một chút vì thế các em ấy muốn kẹo }Graph;
của mình nhiều hơn của bạn.
Hãy viết chương trình giúp cô tính xem mỗi em void make_null_list(List* L) {
cần được chia ít nhất bao nhiêu kẹo và tổng số L->size = 0;
kẹo ít nhất mà cô phải chuẩn bị là bao nhiêu.
}
Đầu vào (Input):
Dữ liệu đầu vào được nhập từ bàn phím với định /* Them mot phan tu vao cuoi danh sach */
dạng:
void push_back(List* L, ElementType x) {
- Dòng đầu tiên chứa 2 số nguyên n và m, tương
ứng là số bé và số cặp bé mà trong đó có 1 bé L->data[L->size] = x;
muốn có kẹo hơn bạn mình..
L->size++;
- m dòng tiếp theo mỗi dòng chứa 2 số nguyên a,
b nói rằng bé a muốn có kẹo nhiều hơn bé b. }

Đầu ra (Output): /* Lay phan tu tai vi tri i, phan tu bat


dau o vi tri 1 */
In ra màn hình số kẹo ít nhất của từng em, mỗi
em trên một dòng. ElementType element_at(List* L, int
Dòng cuối cùng in tổng số kẹo ít nhất mà cô giáo i) {
Trang cần phải chuẩn bị
return L->data[i-1];
Chú ý:
}
Xem thêm các ví dụ để hiểu thêm về đầu vào và
đầu ra. /* Tra ve so phan tu cua danh sach */
int count_list(List* L) {
#include <stdio.h> return L->size;
#define MAX_VERTICES 100 }
#define MAX_ELEMENTS 100 /* phan do thi */
typedef int ElementType; void init_graph(Graph *G,int n){
int rank[MAX_VERTICES]; int i,j;
int d[MAX_VERTICES]; G->n = n;
for(i=1;i<=n;i++)
//khai bao cau truc list for(j=1;j<=n;j++)
typedef struct { G->A[i][j]=0;
ElementType data[MAX_ELEMENTS]; }
45
void add_edge(Graph *G, int x,int y){
G->A[y][x]=1; void ranking(Graph *G){
} int x, u;
int adjacent(Graph *G, int x, int y){ for(u = 1; u <= G->n; u++){
return G->A[x][y] != 0; d[u] = 0;
} // rank[u] = 0;
int degree(Graph *G,int x){ }
int y,deg=0; for(x = 1; x <= G->n; x++)
for(y=1; y<= G->n; y++) for(u = 1; u <= G->n; u++)
deg+= G->A[x][y]; if(G->A[x][u] != 0)
return deg; d[u]++;
} // d[1]=0;
List neighbors(Graph *G, int x){ // List S1, S2;
int y; make_null_list(&S1);
List list; for(u = 1; u <= G->n; u++)
make_null_list(&list); if(d[u] == 0)
for(y=1;y<=G->n;y++) push_back(&S1, u);
if(adjacent(G,x,y)) // int k = 1, i;
push_back(&list,y); int i;
return list; while(S1.size > 0){
} make_null_list(&S2);
void copy_list(List *S1, List *S2){ for(i = 1; i <= S1.size; i++){
int i, x; int u = element_at(&S1, i);
make_null_list(S1); rank[u] = k;
for(i=1;i<=S2->size;i++){ int v;
x=element_at(S2,i); for (v = 1; v <= G->n; v++)
push_back(S1,x); if(G->A[u][v] != 0){
} d[v]--;
} if(d[v] == 0)
push_back(&S2, v);
int k=1; }
List S1, S2; }
46
copy_list(&S1, &S2);
k++;
}
}
int main (){
// freopen("dt.txt", "r", stdin);
Graph G; Người ta cần:
- Xác định thời điểm sớm nhất và trể nhất để bắt đầu
int n, m, a, b, e; cho mỗi công việc mà không ảnh hưởng đến tiến độ
của dự án
int sum=0;
Để đơn giản trong cài đặt, ta đánh số lại các công việc
scanf("%d%d", &n, &m); theo thứ tự 1, 2, 3 ...thay vì A, B, C. . .như sau:

init_graph(&G, n);
for (e = 0; e < m; e++) {
scanf("%d%d", &a, &b); và lưu vào tập tin theo định dạng như bảng giá
trị đầu vào
add_edge(&G, a, b);
Hãy viết chương trình tìm thời gian sớm nhất hoàn
} thành dự án và Thời điểm sớm nhất và trể nhất để
bắt đầy cho mỗi công việc của dự án mà không ảnh
ranking(&G); hưởng đến tiến độ của dự án.
for(a=1;a<=n;a++){ Đầu vào:

printf("%d \n",rank[a]); 10
70
sum =sum + rank[a]; 310
120
} 810
2340
printf("%d",sum);
1340
return 0; 1340
260
} 280
15790

4. TỔ CHỨC THI CÔNG – DỰ ÁN XÂY


NHÀ
Có một dự án xây nhà với 10 công việc được cho như
bảng sau:

47
Dòng đầu tiên là số công việc (10), các dòng tiếp theo G->A[u][v] += 1;
mỗi dòng mô tả một công việc bao gồm d[u]: thời
gian hoàn thành công việc u và danh sách các công }
việc trước đó của u. Danh sách được kết thúc bằng
số int adjacent(Graph *G, int x, int y){
0. Ví dụ: công việc 1 (công việc A) có d[1] = 7 và danh
return G->A[x][y] != 0 ;
sách các công việc trước đó
rỗng. }
Công việc 2 (công việc B) có d[2] typedef int ElementType;
= 3 và danh sách công việc
trước đó là {1}. typedef struct{
Đầu ra:
ElementType data[MAX];
Dòng đầu tiên: Thời gian sớm
int size;
nhất hoàn thành dự án
Mỗi dòng tiếp theo: In ra thời }List;
gian sớm nhất và thời trể nhất
để bắt đầu cho mỗi công việc (1 void make_null_list(List *L){
=> n+2, gồm cả công việc alpha
và beta) L->size = 0;

t(u)-T(u) }
Chú ý đọc dữ liệu: void push_back(List *L, int x){
L->data[L->size]=x;
L->size++;
}
ElementType element_at(List *L, int i){
return L->data[i-1];
}

#include<stdio.h> List neighbors(Graph *G, int x){

#define MAX 100 int y;

#define oo 9999999 List list;

typedef struct{ make_null_list(&list);

int n,m; for(y=1;y<=G->n;y++)

int A[MAX][MAX]; if(adjacent(G,x,y))

}Graph; push_back(&list,y);

void init_graph(Graph *G, int n){ return list;

G->n=n; }

G->m=0; void copy_list(List *L1,List *L2){

} make_null_list(L1);

void add_edge(Graph *G, int u,int v){ int i;


48
for(i=1;i<=L2->size;i++){ int max(int a , int b){
int u = element_at(L2,i); if(a>b) return a ;
push_back(L1,u); else return b;
} }
} int d[MAX];
typedef struct{ void topo_sort(Graph *G,List *L){
int front,rear; int d[100];
int data[MAX]; int x, u;
}Queue; Queue Q;
void make_null_queue(Queue *Q){ // khoi tao hang doi rong
Q->front = 0; make_null_queue(&Q);
Q->rear = -1; for (u = 1; u <= G->n; u++)
} d[u] = 0;
void push(Queue *Q, int x){ for (x = 1; x <= G->n; x++)
Q->rear++; for (u = 1; u <= G->n; u++)
Q->data[Q->rear] = x; if (G->A[x][u] != 0) // co duong di tu x->u
} d[u]++;
int top(Queue *Q){ for(u=1;u<=G->n;u++)
return Q->data[Q->front]; if(d[u]==0) push(&Q,u);
} make_null_list(L); while(!
int pop(Queue *Q){ empty(&Q)){
return Q->front++; int u = top(&Q);
} pop(&Q);
int empty(Queue *Q){ push_back(L,u);
return Q->front > Q->rear; List list = neighbors(G,u);
} for(x=1; x<=list.size; x++){
// Ham min int v = element_at(&list,x);
int min(int a,int b){ d[v]--;
if(a<b) return a ; if(d[v]==0)
else return b; push(&Q,v);
} }
// Ham max }

49
} if (deg_neg == 0)
int main(){ add_edge(&G, n+1, u);
Graph G; }
int n, u, x, v, j ; //--Noi dinh khong co cung di ra den dinh n+2
List L; for (u = 1; u <= n; u++) {
//FILE* file = fopen("dt.txt", "r"); int deg_pos = 0;
scanf("%d", &n); for (v = 1; v <= n; v++)
init_graph(&G, n+2); // them 2 dinh alpha & beta if (G.A[u][v] > 0)
d[n+1] = 0; // d[alpha] = 0 deg_pos++;
for (u = 1; u <= n; u++) { if (deg_pos == 0)
scanf("%d",&d[u]); add_edge(&G, u, n+2);
do { }
scanf("%d", &x); //--Kiem tra du lieu doc tu file, xoa sau khi kiem tre
if (x > 0) add_edge(&G, x, u); ket qua dung

}while (x > 0); // printf("\n");

} // for (i = 1; i <= n+2; i++) {

//--Kiem tra du lieu doc tu file, xoa sau khi kiem tre // for (j = 1; j <= n+2; j++)
ket qua dung // printf("%d ", G.A[i][j]);
// for (i = 1; i <= n; i++) printf("%d ",d[i]); // printf("\n");
// printf("\n"); // }
// for (i = 1; i <= n; i++) { //----------------------------------------------------------------
// for (j = 1; j <= n; j++) --

// printf("%d ", G.A[i][j]); topo_sort(&G,&L);

// printf("\n"); int t[100];

// } //Kiem tra L, sau do xoa

// // printf("\n");
-- // for (i = 1; i <= L.size; i++)
//--Noi dinh n+1 voi dinh khong co cung di den no // printf("%d ",element_at(&L, i));
for (u = 1; u <= n; u++) { // printf("\n");
int deg_neg = 0; //Xac dinh thoi diem som nhat
for (x = 1; x <= n; x++) t[n+1] = 0; // t[alpha] = 0
if (G.A[x][u] > 0) // co duong di tu x->u for (j = 2; j <= L.size; j++) {
deg_neg++; int u = element_at(&L, j);
50
t[u] = 0; việc E mình bắt đầu làm vào ngày thứ 60 thì tổng
for (x = 1; x <= G.n; x++) thời gian thực hiện dự án có bị ảnh hưởng
không?" "Nếu công việc H mình bắt đầu làm vào
if (G.A[x][u] > 0) ngày thứ 50 thì tổng thời gian thực hiện dự án có bị
ảnh hưởng không?". Anh ta hỏi mọi người hoài
t[u] = max(t[u], t[x] + d[x]); những câu hỏi tương tự như thế làm cho các thành
viên trong nhóm bực bội. Biết rằng dựa vào bảng
}
công việc người ta có thể xác định thời điểm sớm
//Xac dinh thoi diem tre nhat nhất và trể nhất để bắt đầu cho mỗi công việc mà
không ảnh hưởng đến tiến độ của dự án phần mềm.
int T[100]; Hãy viết chương trình để giúp anh Tuấn tự trả lời
câu hỏi của mình.
T[n+2] = t[n+2];
Để đơn giản trong cài đặt, ta đánh số lại các công việc
for (j = L.size - 1; j >= 1; j --) { theo thứ tự 1, 2, 3 thay vì A, B, C và lưu vào tập tin
theo định dạng như sau:
int u = element_at(&L, j);
T[u] = oo;
for (v = 1; v <= G.n; v++) Dòng đầu tiên là số công việc (12), các
if (G.A[u][v] > 0) dòng tiếp theo mỗi dòng mô tả một
công việc bao gồm d[u]: thời gian
T[u] = min(T[u], T[v] - d[u]); hoàn thành công việc u và danh sách
} các công việc trước đó của u. Danh
sách được kết thúc bằng số 0. Ví dụ:
printf("%d\n",t[n+2]);
công việc 1 (công việc A) có d[1] = 14
for(u=1; u<=G.n; u++) và danh sách các công việc trước đó
rỗng.
printf("%d-%d\n",t[u],T[u]);
Công việc 2 (công việc B) có d[2] = 12
return 0; và danh sách công việc trước đó là
{1}.
}
Dòng cuối cùng: công việc u và thời
gian bắt đầu t, hai giá trị u và t tương ứng với câu hỏi
5. TỔ CHỨC THI CÔNG – DỰ ÁN của anh Tuấn: "Nếu công việc u mình bắt đầu làm
PHẦN MỀM vào ngày thứ t thì tổng thời gian thực hiện dự án có
bị ảnh hưởng không?"
Việc thực hiện một dự án phát triển phần mềm được
bố trí thành các công việc và thời gian thực hiện như
sau: Đầu ra:

- Anh Tuấn là một thành viên trong nhóm phát triển


phần mềm. Anh ta thường hay hỏi mọi thành viên
trong nhóm các câu hỏi tương tự như sau: "Nếu công

51
Yes: Nếu ngày bắt đầu thực hiên công việc nằm }
trong thời điểm sớm nhất và trể nhất để bắt đầu công
việc tương ứng. /* Them cung e = (x, y) vao do thi G */

No: Nếu ngày bắt đầu thực hiện void add_edge(Graph* G, int x, int y) {
công việc KHÔNG nằm trong thời G->A[x][y] = 1; //y ke voi x
điểm sớm nhất và trể nhất để bắt
đầu công việc tương ứng. }
Ví dụ: Công việc 5, mình có thể
bắt đầu làm vào ngày thứ 60 được /* Kiem tra y co ke voi x khong */
hay không? => YES (Vì Thời gian
int adjacent(Graph* G, int x, int y) {
sớm nhất và thời gian trể nhất
thực hiện công việc 5 là: 56-71, 60 return G->A[x][y] != 0;
nằm trong khoảng thời gian cho
phép) }
Chú ý đọc dữ liệu: /* Tinh bac cua dinh x: deg(x) */
int degree(Graph* G, int x) {
int y, deg = 0;
for (y = 1; y <= G->n; y++)
if (G->A[x][y] > 0)
deg+=G->A[x][y];
return deg;

}
#include <stdio.h> /* KHAI BAO VA DINH NGHIA CTDL DANH SACH
*/
#include <stdio.h>
#define MAX_ELEMENTS 100
#define MAX_VERTICES 100
typedef int ElementType;
typedef struct {
typedef struct {
int n; /* n: so dinh */
ElementType data[MAX_ELEMENTS];
/* ma tran dinh – dinh */
int size;
int A[MAX_VERTICES][MAX_VERTICES];
} List;
} Graph;
/* Tao danh sach rong */
/* Khoi tao do thi G co n dinh */
void make_null_list(List* L) {
void init_graph(Graph* G, int n) {
L->size = 0;
int i, j;
}
G->n = n;
/* Them mot phan tu vao cuoi danh sach */
for (i = 1; i <= n; i++)
void push_back(List* L, ElementType x) {
for (j = 1; j <= n; j++)
L->data[L->size] = x;
G->A[i][j] = 0;

52
L->size++; if (a>b) return a;
} else return b;
/* Lay phan tu tai vi tri i, phan tu bat dau o vi tri }
1*/ ElementType element_at(List* L, int i) { /* Khai bao Queue */
return L->data[i-1]; #define MAX_ELEMENTS 100
} typedef struct {
/* Tra ve so phan tu cua danh sach */ int data[MAX_ELEMENTS];
int count_list(List* L) { int front, rear;
return L->size; } Queue;
} void make_null_queue(Queue* Q) {
/* Tim cac dinh ke cua dinh x */ Q->front = 0;
List neighbors(Graph* G, int x) { Q->rear = -1;
int y; }
List list; void push(Queue* Q, int x) {
make_null_list(&list); Q->rear++;
for (y = 1; y <= G->n; y++) Q->data[Q->rear] = x;
if (adjacent(G, x, y)) }
push_back(&list, y); int top(Queue* Q) {
return list; return Q->data[Q->front];
} }
void copy_list(List *L1, List *L2){ void pop(Queue* Q) {
make_null_list(L1); Q->front++;
int i; }
for(i=1;i<=L2->size;i++) int empty_queue(Queue* Q) {
push_back(L1,element_at(L2,i)); return Q->front > Q->rear;
} }
#define oo 9999999 int d[MAX_VERTICES];
int min(int a, int b){ void topo_sort(Graph *G,List *L){
if(a>=b) return b; //Tinh d[u]
else return a; int d[MAX_VERTICES];
} int x, u;
int max(int a, int b){ for (u = 1; u <= G->n; u++)
53
d[u] = 0; for (u = 1; u <= n; u++) {
for (x = 1; x <= G->n; x++) scanf("%d",&d[u]);
for (u = 1; u <= G->n; u++) do {
if (G->A[x][u] != 0) scanf("%d", &x);
d[u]++; if (x > 0) add_edge(&G, x, u);
Queue Q; }while (x > 0);
make_null_queue(&Q); }
for(u=1;u<=G->n;u++) for (u = 1; u <= n; u++) {
if(d[u]==0) int deg_neg = 0;
push(&Q,u); for (x = 1; x <= n; x++)
make_null_list(L); while(! if (G.A[x][u] > 0)
empty_queue(&Q)){ deg_neg++;
int u = top(&Q); if (deg_neg == 0)
pop(&Q); add_edge(&G, n+1, u);
push_back(L,u); }
List list = neighbors(G,u); for (u = 1; u <= n; u++) {
for(x=1;x<=list.size;x++){ int deg_pos = 0;
int v = element_at(&list,x); for (v = 1; v <= n; v++)
d[v]--; if (G.A[u][v] > 0)
if(d[v]==0) deg_pos++;
push(&Q,v); if (deg_pos == 0)
} add_edge(&G, u, n+2);
} }
} topo_sort(&G,&L);
int main() { int t[MAX_VERTICES];
Graph G; t[n+1] = 0;
int n, u, x, v, j ; for (j = 2; j <= L.size; j++) {
List L; int u = element_at(&L, j);
// FILE* file = fopen("duanphanmem1.txt", "r"); t[u] = -1;
scanf("%d", &n); for (x = 1; x <= G.n; x++)
init_graph(&G, n+2); if (G.A[x][u] > 0)
d[n+1] = 0; t[u] = max(t[u], t[x] + d[x]);
54
} In ra màn hình trọng số của cây khung tìm được và
int T[MAX_VERTICES]; danh sách các cung theo thứ tự tăng dần của trọng
số.
T[n+2] = t[n+2]; Quy ước: các cung được in ra theo định dạng:
for (j = L.size-1 ; j >= 1; j --) { uvw
int u = element_at(&L, j); với (u < v), mỗi cung trên 1 dòng. Nếu hai cung có
trọng số bằng nhau thì cung nào có u nhỏ hơn sẽ
T[u] = oo; được in trước. Nếu có trọng số bằng nhau và u giống
nhau thì cung nào có v nhỏ hơn sẽ in trước.
for (v = 1; v <= G.n; v++)
Xem thêm ví dụ bên dưới.
if (G.A[u][v] > 0)
Chú ý:
T[u] = min(T[u], T[v] - d[u]);
 Để chạy thử chương trình, bạn nên tạo một
} tập tin dt.txt chứa đồ thị cần kiểm tra.
//printf("%d\n",t[G.n]);  Thêm dòng freopen("dt.txt", "r", stdin); vào
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
int a,b; dòng này ra.
 Có thể sử dụng đoạn chương trình đọc dữ
scanf("%d %d",&a,&b); liệu mẫu sau đây:
/* for(u =1; u<=G.n;u++){
freopen("dt.txt", "r", stdin); //Khi nộp bài
if (t[u]==T[u]) nhớ bỏ dòng này.
Graph G;
printf("%d\n",u); int n, m, u, v, w, e;
scanf("%d%d", &n, &m);
printf("%d-%d\n",t[u],T[u]);*/
init_graph(&G, n);
// }
for (e = 0; e < m; e++) {
if(b>= t[a] && b<= T[a])
scanf("%d%d%d", &u, &v,
printf("YES"); &c);
add_edge(&G, u, v,
else printf("NO"); w) ;
return 0; }

}
#include <stdio.h>
1. TÌM CÂY KHUNG BẰNG GIẢI THUẬT
#define MAX 100
KRUSKAL
// Do Thi G ma tran Ke
Viết chương trình tìm cây khung có trọng số nhỏ
nhất bằng giải thuật Kruskal. typedef struct {
Đầu vào: int x,y,z;
Dữ liệu đầu vào được nhập từ bàn phím với định
dạng }Edge;

- Dòng đầu tiên chứa 2 số nguyên n và m, tương typedef struct {


ứng là số đỉnh và số cung.
int n,m;
- m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v,
w mô tả cung (u, v) có trọng số w. Edge Data[MAX];
Đầu ra: }dothi;
55
void khoitao(dothi *G,int n){ int u = G->Data[e].x;
G->n = n; int v = G->Data[e].y;
G->m = 0; int w = G->Data[e].z;
} int root_u = findRoot(u);
void themcung(dothi *G,int x,int y, int W){ int root_v = findRoot(v);
G->Data[G->m].x = x; if(root_u != root_v){
G->Data[G->m].y = y; if (u<v)
G->Data[G->m].z = W; themcung(T,u,v,w);
G->m++; else themcung(T,v,u,w);
} parent[root_v] = root_u;
int parent[100]; sum_w += w;
int findRoot (int u){ }
if (parent[u] == u) }
return u; return sum_w;
return findRoot(parent[u]); }
} int main(){
int Kruskal (dothi *G, dothi *T){ //freopen("dt.txt", "r", stdin); //Khi n?p bài nh? b?
int i,j,e,u; dòng này.

Edge temp; dothi G,T;

for (i=0;i< G->m;i++) int n, m, u, v, w, e;

for(j=i+1;j< G->m; j++){ scanf("%d%d", &n, &m);

if(G->Data[i].z > G->Data[j].z){ khoitao(&G, n);

temp = G->Data[i]; //khoitao(&T, n);

G->Data[i] = G->Data[j]; for (e = 0; e < m; e++) { scanf("%d

G->Data[j] = temp; %d%d", &u, &v, &w);

} themcung(&G, u, v, w);

} }

khoitao(T,G->n); int sum_w = Kruskal(&G,&T);

for (u=1;u <= G->n; u++) printf("%d \n",sum_w);

parent[u] = u; for (e=0;e <T.m ; e++)

int sum_w = 0; printf("%d %d %d


\n",T.Data[e].x,T.Data[e].y,T.Data[e].z);
for(e=0;e < G->m ; e++){
}
56
typedef struct{
2. TÌM CÂY KHUNG CÓ TRỌNG LƯỢNG NHỎ int u,v,w;
NHẤT BẰNG GIẢI THUẬT PRIM
}Edge;
Viết chương trình tìm cây khung có trọng số nhỏ
nhất bằng giải thuật Prim. typedef struct{

Đầu vào (Input): int n,m;


Dữ liệu đầu vào được nhập từ bàn phím với định int A[MAX_VERTICES][MAX_VERTICES];
dạng
}Graph;
- Dòng đầu tiên chứa 2 số nguyên n và m, tương ứng
là số đỉnh và số cung. void init_graph(Graph *G,int n){
- m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v, G->n = n;
w mô tả cung (u, v) có trọng số w.
int i,j;
Đầu ra (Output):
In ra màn hình theo định dạng sau: for(i=1; i<=n; i++)

- Dòng đầu tiên in trọng số của cây khung tìm được for(j=0; j<n; j++)
- n - 1 dòng kế tiếp in ra các cung của cây tìm tìm G->A[i][j]=0;
được theo định dạng: u v w. Cung (u1, v1) sẽ được
in ra trước cung (u2, v2) nếu (u1 < u2) hoặc (u1 = u2 }
và v1 < v2).
void add_edge(Graph *G,int x, int y, int w){
Xem thêm ví dụ bên dưới.
G->A[x][y]+=w;
Chú ý:
G->A[y][x]+=w;
 Để chạy thử chương trình, bạn nên tạo một
tập tin dt.txt chứa đồ thị cần kiểm tra. }
 Thêm dòng freopen("dt.txt", "r", stdin); vào
void swap(Edge *a, Edge *b){
ngay sau hàm main(). Khi nộp bài, nhớ gỡ bỏ
dòng này ra. Edge t;
 Có thể sử dụng đoạn chương trình đọc dữ
liệu mẫu sau đây: t=*a;
*a=*b;
freopen("dt.txt", "r",
stdin); //Khi nộp bài nhớ bỏ dòng này. *b=t;
Graph G;
int n, m, u, v, w, e; }
scanf("%d%d", &n, &m);
init_graph(&G, n); int nho_hon(Edge a, Edge b){
if((a.u<b.u) || (a.u==b.u) || (a.v<b.v))
for (e = 0; e < m; e++) {
scanf("%d%d%d", &u, &v, &c); return 1;
add_edge(&G, u, v, w);
} return 0;
#include<stdio.h> }
#define MAX_LENGTH 100 void bubble_sort(Edge e[], int n){
#define MAX_VERTICES 100 int i,j;
#define MAX_EDGES 500 for(i=0; i<=n-1; i++)

57
for(j=n-1; j>i; j--) }
if(nho_hon(e[j], e[j-1]))
swap(&e[j], &e[j-1]);
}
typedef struct{
int data[MAX_LENGTH];
int size;
}List;
void make_null_list(List* L){
L->size = 0;
}
int empty_list(List L){
return L.size==0;
}
void push_back(List* L, int x){
L->data[L->size] = x;
L->size++;
}
int element_at(List* L, int i){
return L->data[i-1];
}
int distancefrom(int u, List L, Graph G){
int min_dist = 9999;
int min_v = -1;
int i;
for(i=1; i<=L.size; i++){
int v =
element_at(&L,i);
if(G.A[u][v]!=0 && min_dist>G.A[u][v]){
min_dist=G.A[u][v];
min_v=v;
}

58
return min_v; (&L,1); mark[1] =
} 1; for(i=1; i<G.n; i+
int +){
check(List int min_dist=9999, min_u, min_v;
L, int x){ for(u=1; u<=G.n; u++)
int i; if(mark[u]==0){

for(i=1; i<=L.size; i++) int v = distancefrom(u,L,G);


if(x==element_at(&L,i)) if(v!=-1 && G.A[u][v]<min_dist){
return 1; min_dist = G.A[u][v];
return 0; min_u = u;
} min_v = v;
Edge edges[dem].u = v;
edges[ edges[dem].v = u;
100];
int
dem=0
;
int mark[100];
int prim(Graph G,
Graph T){
init_graph(&T,
G.n);
List L;
make_null_
list(&L);
int u,i,
sum_w=0;
for(i=1;
i<G.n; i++)
mark[i]
= 0;
push_back

59
edges[dem].w = min_dist; Viết chương trình tìm luồng cực đại trên mạng bằng
dem++; thuật toán Ford - Fullkerson (duyệt theo chiều rộng).
Đầu vào (Input):
}
Dữ liệu đầu vào được nhập từ bàn phím với định
} dạng:
push_back(&L, min_u); - Dòng đầu tiên chứa 2 số nguyên n và m, tương ứng
là số đỉnh và số cung.
mark[min_u]=1;
- m dòng tiếp theo mỗi dòng chứa 3 số nguyên u, v, w
add_edge(&T, min_u, min_v, min_dist); mô tả cung (u, v) có trọng số w.

sum_w += min_dist; Đầu ra (Output):


In ra màn hình theo định dạng sau:
}
- Dòng đầu tiên in luồng cực đại theo dạng: Max
return sum_w; flow: s
} - Dòng thứ hai in các đỉnh của X0 theo dạng: X0: x1
x2 ..., mỗi đỉnh cách nhau 1 khoảng trắng
int main(){
- Dòng thứ ba in các đỉnh của Y0 theo dạng: Y0: y1
Graph G,T; y2 ..., mỗi đỉnh cách nhau 1 khoảng trắng
int n,m,u,v,w,i; Xem thêm ví dụ bên dưới.
Chú ý:
//freopen("dothi.txt","r",stdin);
scanf("%d%d", &n, &m);  Để chạy thử chương trình, bạn nên tạo một
tập tin dt.txt chứa đồ thị cần kiểm tra.
init_graph(&G,n);  Thêm dòng freopen("dt.txt", "r", stdin); vào
ngay sau hàm main(). Khi
int e;
nộp bài, nhớ gỡ bỏ dòng
for(e=0; e<m; e++){ scanf("%d%d này ra.
 Có thể sử dụng đoạn
%d", &u, &v, &w); chương trình đọc dữ liệu
mẫu sau đây:
add_edge(&G,u,v,w);
} freopen("dt.txt", "r",
stdin); //Khi nộp bài nhớ bỏ dòng
int sum_w = prim(G,T); này.
Graph G;
printf("%d", sum_w); int n, m, u, v, w, e;
scanf("%d%d", &n,
bubble_sort(edges, dem); &m);
for(i=0; i<dem; i++) init_graph(&G, n);

printf("\n%d %d %d", edges[i].u, edges[i].v, for (e = 0; e < m; e++) {


edges[i].w); scanf("%d%d%d", &u, &v, &c);
add_edge(&G, u, v, w);
return 0; }
#include <stdio.h>
}
#define MAXN 100
#define NO_EDGE 0
3. TÌM LUỒNG CỰC ĐẠI TRONG MẠNG
#define INF 999999
60
typedef struct { int top(Queue *Q) {
int C[MAXN][MAXN]; return Q->data[Q->front];
int F[MAXN][MAXN]; }
int n; void dequeue(Queue * Q){
}Graph; Q->front++;
void init_graph(Graph *G,int n){ }
G->n=n; int empty (Queue* Q) {
} return Q->front>Q->rear;
typedef struct { }
int dir; int min (int a, int b) {
int pre; return a < b ? a : b;
int sigma; }
}Label; int FordFullkerson (Graph* G, int s, int t) {
Label labels[MAXN]; init_flow(G);
void init_flow(Graph *G) { int u,v,sum_flow = 0;
int u, v; Queue Q;
for (u = 1; u <=G->n; u++) do {
for (v = 1; v <= G->n; v++) for (u = 1; u <= G->n; u++)
G->F[u][v] = 0; labels[u].dir = 0;
} labels[s].dir = 1;
typedef struct { labels[s].pre = s;
int data[MAXN]; labels[s].sigma = INF;
int front, rear; make_null_queue(&Q);
}Queue; enqueue(&Q,s);
void make_null_queue (Queue* Q) { int found = 0; while(!
Q->front = 0; empty(&Q)) {
Q->rear = -1; int u = top (&Q);
} dequeue(&Q);
void enqueue (Queue *Q, int x) { for (v = 1; v <= G->n ; v++) {
Q->rear++; if (labels[v].dir == 0 && G->C[u][v] !=
Q->data[Q->rear] = x; NO_EDGE && G->F[u][v] < G-> C[u][v] ) {

} labels[v].dir = 1;

61
labels[v].pre = u; return sum_flow;
labels[v].sigma = min(labels[u].sigma, G- }
>C[u][v] - G->F[u][v]);
int main() {
enqueue(&Q,v);
// ffreopen("dt.txt", "r", stdin); //Khi n?p bài nh? b?
} dòng này.
if (labels[v].dir == 0 && G->C[v][u] != Graph G;
NO_EDGE && G->F[v][u] > 0) {
int n, m, u, v, c ,e;
labels[v].dir = -1;
scanf("%d%d", &n, &m);
labels[v].pre = u;
init_graph(&G, n);
labels[v].sigma = min(labels[u].sigma, G-
for (e = 0; e < m; e++) { scanf("%d
>F[u][v]);
%d%d", &u, &v, &c);
enqueue(&Q,v);
G.C[u][v] = c;
}
}
}
if(labels[t].dir != 0) { int max_flow = FordFullkerson(&G,1,n);
found = 1; printf ("Max flow: %d \n",max_flow);
break; printf("X0: ");
} for ( e = 1; e <= G.n; e++) {
} if (labels[e].dir != 0)
if (found == 1) { printf("%d ",e);
int x = t; }
int sigma = labels[t].sigma; printf("\nY0: ");
sum_flow += sigma; for (e = 1; e <= G.n; e++) {
while(x!=s) { if(labels[e].dir == 0)
int u = labels[x].pre; printf("%d ",e);
if (labels[x].dir>0) }
G->F[u][x] += sigma; return 0;
else }
G->F[x][u] -= sigma;
x = u;
}
}else break;
}while(1);

62

You might also like