LỜI NÓI ĐẦU
Ngày nay, cùng với sự phát triển của đất nước ngành Công nghệ thông tin đã có những
bước phát triển mạnh mẽ không ngừng và tin học đã trở thành chiếc chìa khóa dẫn đến
thành công cho nhiều cá nhân trong nhiều lĩnh vực, hoạt động. Công nghệ thông tin là
ngành sử dụng máy tính và phần mềm máy tính để chuyển đổi, lưu trữ, bảo vệ, xử lý,
truyền, và thu thập thông tin.
Trên thế giới cũng như ở Việt Nam , công nghệ thông tin đã trở thành một ngành
công nghiệp mũi nhọn, nó là một ngành khoa học kỹ thuật không thể thiếu trong việc áp
dụng vào các hoạt động xã hội như: Quản lý, kinh tế, thông tin
Với sự ra đời của Internet, tất cả các trường học, áp dụng của kiến thức, kỹ năng và
hiểu biết về Công nghệ thông tin trong các môn học. Hiện nay hầu hết các ngành học đều
áp dụng Công nghệ thông tin trong chuyên ngành của mình nhằm nâng cao hiệu quả dạy
và học.
Trong khoa học máy tính và trong toán học, thuật toán tìm đường đi ngắn nhất
trong đồ thị là một bài toán thường được vận dụng trong các ứng dụng tin học, và là một
yêu cầu không thể thiếu trong khi thiết kế các phần mềm như xây dựng bản đồ mạng
lưới giao thông.
Mặc dù rất cố gắng để hoàn thành đề tài, xong thời gian có hạn và kinh nghiệm
chưa có nhiều nên bài báo cáo của em còn có nhiều thiếu xót cần được bổ xung . Vì vậy,
e mong nhận được ý kiến đóng góp của thầy cô và bạn bè để đề tài ngày càng hoàn thiện
tốt hơn.
Cuối cùng, em xin chân thành cảm ơn Thầy Bùi Anh Tú giảng viên bộ môn công
nghệ phần mềm Trường Đại học Công nghệ Thông tin và Truyền thông đã tận tình chỉ
bảo hướng dẫn em hoàn thành đề tài này.!
Sinh viên thực hiện
Đỗ Thị Huyền
MỤC LỤC
1
MỤC LỤC 2
1.1.Xác định và mô hình hóa bài toán cần giải quyết 3
1.2. Cài đặt chương trình cho bài toán cần giải quyết 3
1.3. Sơ lược về C# 4
1.3.2 . Nền tảng ngôn ngữ C# 8
1.3.2.1 . Kiểu dữ liệu 8
1.3.2.3 . Câu lệnh 10
1.3.2.4 . Toán tử 14
1.4 . Lớp, đối tượng 16
1.4.1 Cấu trúc chương trình của C# 17
2.1. Các khái niệm cơ bản của lý thuyết đồ thị 18
2.1.1. Định nghĩa đồ thị 18
2.1.3. Định nghĩa đường đi, chu trình , đồ thị liên thông 21
2.3.1 Phân tích 34
2.3.2 Giải thuật tìm đường đi ngắn nhất giữa một cặp đỉnh 34
Định nghĩa 1.0. 34
2.3.3 Giải thuật Dijkstra 34
2.3.3.1 Nội dung 34
Có rất nhiều giải thuật đã được phát triển để giải bài toán tìm đường đi ngắn nhất giữa một
cặp đỉnh, trong khuôn khổ bài viết này em chỉ xin giới thiệu giải thuật Dijkstra. Giải thuật
Dijkstra là một giải thuật để giải bài toán đường đi ngắn nhất nguồn đơn trên một đồ thị có
trọng số cạnh mà tất cả các trọng số đều không âm. Nó xác định đường đi ngắn nhất giữa hai
đỉnh cho trước, từ đỉnh a đến đỉnh b 34
2.3.3.2 Độ phức tạp của giải thuật Dijkstra 35
37
KẾT LUẬN 38
TÀI LIỆU THAM KHẢO 40
CHƯƠNG 1: CƠ SỞ LÝ THUYẾT
Để giải một bài toán trong thực tế bằng máy tính ta thường trải qua 2 giai đoạn:
• Giai đoạn 1: Xác định bài toán cần giải quyết và xây dựng mô hình toán học cho
bài toán. Mục đích trả lời 2 câu hỏi: “Bài toán cho cái gì? Và yêu cầu làm những
gì?” sau đó trả lời cho câu hỏi: “để thực hiện các yêu cầu của bài toán thì làm như
thế nào?”.
• Giai đoạn 2: Cài đặt chương trình: sử dụng ngôn ngữ lập trình để xây dựng
chương trình tương ứng với cách làm ở giai đoạn trước đó.
1.1. Xác định và mô hình hóa bài toán cần giải quyết.
Khi giải quyết một bài toán thực tế, ta phải bắt đầu từ việc xác định bài toán. Nhiểu
thời gian và công sức bỏ ra để xác định bài toán cần giải quyết, tức là phải trả lời rõ rang
câu hỏi: “phải làm gì?” sau đó là “làm như thế nào?”. Thông thường khi khởi đầu hầu hết
các bài toán là không đơn giản, không rõ ràng. Để giảm bớt sự phức tạp của bài toán ta
phải hình thức hóa nó, nghĩa là phát biểu lại bài toán thực tế thành một bài toán hình
thức(hay còn gọi là mô hình toán ).
Một bài toán thực tế bất kì thường bao gồm các đối tượng dữ liệu và các yêu cầu xử
lý trên những đối tượng đó, cho nên trong giai đoạn phân tích và thiết kế, khi xây dựng
mô hình toán học cho bài toán cần chú trọng đến hai vấn đề:
• Tổ chức biểu diễn các đối tượng dữ liệu của bài toán trong mô hình toán học như
thế nào?.
• Xây dựng các thao tác xử lý trên các đối tượng của mô hình ra sao.
1.2. Cài đặt chương trình cho bài toán cần giải quyết.
Khi cài đặt chương trình giải quyết bài toán tương ứng ta cần quan tâm tới hai vấn đề:
• Biểu diễn mô hình dữ liệu của bài toán trên máy tính như thế nào để máy tính có
thể hiểu và thực hiện các thao tác trên chúng. Giai đoạn này còn được gọi là xây
dựng cấu trúc dữ liệu cho bài toán.
• Mã hóa các giải thuật xử lý trên các cấu trúc dữ liệu tương ứng để giải quyết các
yêu cầu đặt ra của bài toán.
Ta có thể sử dụng một ngôn ngữ lập trình cụ thể nào đó để cài đặt và mã hóa giải thuật
bởi các câu lệnh trong ngôn ngữ lập trình lựa chọn.
1.3. Sơ lược về C#
1.3.1 Ngôn ngữ C#
Ngôn ngữ C# được phát triển bởi đội ngũ kỹ sư của Microsoft, trong đó người dẫn
đầu là Anders Hejlsberg và Scott Wiltamuth. Cả hai người này điều là những người nổi
tiếng, trong đó Anders Hejlsberg được biết đến là tác giả của Turbo Pascal, một ngôn
ngữ lập trình PC phổ biến. Và ông đứng đầu nhóm thiết kế Borland Delphi, một trong
những thành công đầu tiên của việc xây dựng môi trường phát triển tích hợp (IDE) cho
lập trình client/server.
Ngôn ngữ C# khá đơn giản, chỉ khoảng 80 từ khóa và hơn mười mấy kiểu dữ liệu
được xây dựng sẵn. Tuy nhiên, ngôn ngữ C# có ý nghĩa cao khi nó thực thi những khái
niệm lập trình hiện đại. C# bao gồm tất cả những hỗ trợ cho cấu trúc, thành phần
component, lập trình hướng đối tượng. Những tính chất đó hiện diện trong một ngôn ngữ
lập trình hiện đại. Và ngôn ngữ C# hội tụ đủ những điều kiện như vậy, hơn nữa nó được
xây dựng trên nền tảng của hai ngôn ngữ mạnh nhất là C++ và Java.
Phần cốt lõi hay còn gọi là trái tim của bất cứ ngôn ngữ lập trình hướng đối tượng là
sự hỗ trợ của nó cho việc định nghĩa và làm việc với những lớp. Những lớp thì định
nghĩa những kiểu dữ liệu mới, cho phép người phát triển mở rộng ngôn ngữ để tạo mô
hình tốt hơn để giải quyết vấn đề. Ngôn ngữ C# chứa những từ khóa cho việc khai báo
những kiểu lớp đối tượng mới và những phương thức hay thuộc tính của lớp, và cho việc
thực thi đóng gói, kế thừa, và đa hình, ba thuộc tính cơ bản của bất cứ ngôn ngữ lập trình
hướng đối tượng.
C# cũng hỗ trợ giao diện interface, nó được xem như một cam kết với một lớp cho
những dịch vụ mà giao diện quy định. Trong ngôn ngữ C#, một lớp chỉ có thể kế thừa từ
duy nhất một lớp cha, tức là không cho đa kế thừa như trong ngôn ngữ C++, tuy nhiên
một lớp có thể thực thi nhiều giao diện. Khi một lớp thực thi một giao diện thì nó sẽ hứa
là nó sẽ cung cấp chức năng thực thi giao diện.
Trong ngôn ngữ C#, những cấu trúc cũng được hỗ trợ, nhưng khái niệm về ngữ nghĩa
của nó thay đổi khác với C++. Trong C#, một cấu trúc được giới hạn, là kiểu dữ liệu nhỏ
gọn, và khi tạo thể hiện thì nó yêu cầu ít hơn về hệ điều hành và bộ nhớ so với một lớp.
Một cấu trúc thì không thể kế thừa từ một lớp hay được kế thừa nhưng một cấu trúc có
thể thực thi một giao diện.
• Ưu điểm của ngôn ngữ C#
Nhiều người cho rằng không cần thiết có một ngôn ngữ lập trình mới. Java, C++,
Perl, Microsoft Visual Basic, và những ngôn ngữ khác được nghĩ rằng đã cung cấp tất cả
những chức năng cần thiết.
Ngôn ngữ C# là một ngôn ngữ được dẫn xuất từ C và C++, nhưng nó được tạo từ nền
tảng phát triển hơn. Microsoft bắt đầu với công việc trong C và C++ và thêm vào những
đặc tính mới để làm cho ngôn ngữ này dễ sử dụng hơn. Nhiều trong số những đặc tính
này khá giống với những đặc tính có trong ngôn ngữ Java. Không dừng lại ở đó,
Microsoft đưa ra một số mục đích khi xây dựng ngôn ngữ này. Những mục đích này
được được tóm tắt như sau:
- C# là ngôn ngữ đơn giản
C# loại bỏ một vài sự phức tạp và rối rắm của những ngôn ngữ như Java và c++, bao
gồm việc loại bỏ những macro, những template, đa kế thừa, và lớp cơ sở ảo (virtual base
class).
Ngôn ngữ C# đơn giản vì nó dựa trên nền tảng C và C++. Nếu chúng ta thân thiện
với C và C++ hoặc thậm chí là Java, chúng ta sẽ thấy C# khá giống về diện mạo, cú
pháp, biểu thức, toán tử và những chức năng khác được lấy trực tiếp từ ngôn ngữ C và
C++, nhưng nó đã được cải tiến để làm cho ngôn ngữ đơn giản hơn. Một vài trong các sự
cải tiến là loại bỏ các dư thừa, hay là thêm vào những cú pháp thay đổi.
- C# là ngôn ngữ hiện đại
Những đặc tính như là xử lý ngoại lệ, thu gom bộ nhớ tự động, những kiểu dữ liệu
mở rộng, và bảo mật mã nguồn là những đặc tính được mong đợi trong một ngôn ngữ
hiện đại. C# chứa tất cả những đặc tính trên. Nếu là người mới học lập trình có thể chúng
ta sẽ cảm thấy những đặc tính trên phức tạp và khó hiểu.
- C# là ngôn ngữ hướng đối tượng
Những đặc điểm chính của ngôn ngữ hướng đối tượng (Object-oriented language) là
sự đóng gói (encapsulation), sự kế thừa (inheritance), và đa hình (polymorphism). C# hỗ
trợ tất cả những đặc tính trên.
- C# là ngôn ngữ mạnh mẽ và cũng mềm dẻo
Với ngôn ngữ C# chúng ta chỉ bị giới hạn ở chính bởi bản thân hay là trí tưởng tượng
của chúng ta. Ngôn ngữ này không đặt những ràng buộc lên những việc có thể làm. C#
được sử dụng cho nhiều các dự án khác nhau như là tạo ra ứng dụng xử lý văn bản, ứng
dụng đồ họa, bản tính, hay thậm chí những trình biên dịch cho các ngôn ngữ khác.
- C# là ngôn ngữ ít từ khóa
C# là ngôn ngữ sử dụng giới hạn những từ khóa. Phần lớn các từ khóa được sử dụng
để mô tả thông tin. Chúng ta có thể nghĩ rằng một ngôn ngữ có nhiều từ khóa thì sẽ mạnh
hơn. Điều này không phải sự thật, ít nhất là trong trường hợp ngôn ngữ C#, chúng ta có
thể tìm thấy rằng ngôn ngữ này có thể được sử dụng để làm bất cứ nhiệm vụ nào. Bảng
sau liệt kê các từ khóa của ngôn ngữ C#.
abstract default foreach object sizeof unsafe
as delegate goto operator stackalloc ushort
base do if out static using
bool double implicit override string virtual
break else in params struct volatile
byte enum int private switch void
case event interface protected this while
catch explicit internal public throw
char extern is readonly true
checked false lock ref try
class finally long return typeof
const fixed namespace sbyte uint
continue float new sealed ulong
decimal for null short unchecked
• C# là ngôn ngữ hướng module
Mã nguồn C# có thể được viết trong những phần được gọi là những lớp, những lớp
này chứa các phương thức thành viên của nó. Những lớp và những phương thức có thể
được sử dụng lại trong ứng dụng hay các chương trình khác. Bằng cách truyền các mẫu
thông tin đến những lớp hay phương thức chúng ta có thể tạo ra những mã nguồn dùng
lại có hiệu quả.
• C# và những ngôn ngữ khác
Microsoft nói rằng C# mang đến sức mạnh của ngôn ngữ C++ với sự dễ dàng của
ngôn ngữ Visual Basic.
Mặc dù C# loại bỏ một vài các đặc tính của C++, nhưng bù lại nó tránh được những
lỗi mà thường gặp trong ngôn ngữ C++. Điều này có thể tiết kiệm được hàng giờ hay
thậm chí hàng ngày trong việc hoàn tất một chương trình.
C# cũng từ bỏ ý tưởng đa kế thừa như trong C++. Và sự khác nhau khác là C# đưa
thêm thuộc tính vào trong một lớp giống như trong Visual Basic. Và những thành viên
của lớp được gọi duy nhất bằng toán tử “.” khác với C++ có nhiều cách gọi trong các
tình huống khác nhau.
Một ngôn ngữ khác rất mạnh và phổ biến là Java, giống như C++ và C# được phát
triển dựa trên C. Nếu chúng ta quyết định sẽ học Java sau này, chúng ta sẽ tìm được
nhiều cái mà học từ C# có thể được áp dụng.
Điểm giống nhau C# và Java là cả hai cùng biên dịch ra mã trung gian: C# biên dịch
ra MSIL còn Java biên dịch ra bytecode. Sau đó chúng được thực hiện bằng cách thông
dịch hoặc biên dịch just-in-time trong từng máy ảo tương ứng. Tuy nhiên, trong ngôn
ngữ C# nhiều hỗ trợ được đưa ra để biên dịch mã ngôn ngữ trung gian sang mã máy. C#
chứa nhiều kiểu dữ liệu cơ bản hơn Java và cũng cho phép nhiều sự mở rộng với kiểu dữ
liệu giá trị
Tương tự như Java, C# cũng từ bỏ tính đa kế thừa trong một lớp, tuy nhiên mô hình
kế thừa đơn này được mở rộng bởi tính đa kế thừa nhiều giao diện.
1.3.2 . Nền tảng ngôn ngữ C#
1.3.2.1 . Kiểu dữ liệu
C# là ngôn ngữ lập trình mạnh về kiểu dữ liệu,tức là phải khai báo kiểu của mỗi đối
tượng khi tạo (kiểu số nguyên, số thực, kiểu chuỗi, kiểu điều khiển ) và trình biên dịch
sẽ giúp cho người lập trình không bị lỗi khi chỉ cho phép một loại kiểu dữ liệu có thể
được gán cho các kiểu dữ liệu khác. Kiểu dữ liệu của một đối tượng là một tín hiệu để
trình biên dịch nhận biết kích thước của một đối tượng (kiểu int có kích thước là 4 byte)
và khả năng của nó (như một đối tượng button có thể vẽ, phản ứng khi nhấn, ).
• Kiểu dữ liệu xây dựng sẵn
Kiểu C# Số byte Kiểu .NET Mô tả
byte 1 Byte Số nguyên dương không dấu từ 0-255
char 2 Char Ký tự Unicode
bool 1 Boolean Giá trị logic true/ false
sbyte 1 Sbyte Số nguyên có dấu ( từ -128 đến 127)
short 2 Int16 Số nguyên có dấu giá trị từ -32768 đến 32767.
ushort 2 Uint16 Số nguyên không dấu: 0 – 65.535
int 4 Int32 Số nguyên có dấu –2.147.483.647 và2.147.483.647
uint 4 Uint32 Số nguyên không dấu: 0 – 4.294.967.295
float 4 Single
Kiểu dấu chấm động, giá trị xấp xỉ từ 3,4E-38 đến
3,4E+38, với 7 chữ số có nghĩa
double 8 Double
Kiểu dấu chấm động có độ chính xác gấp đôi, giá trị
xấp xỉ từ 1,7E-308 đến 1,7E+308, với 15,16 chữ số
có nghĩa.
decimal 8 Decimal
Có độ chính xác đến 28 con số và giá trị thập phân,
được dùng trong tính toán tài chính, kiểu này đòi hỏi
phải có hậu tố “m” hay “M” theo sau giá trị.
long 8 Int64
Kiểu số nguyên có dấu có giá trị trong khoảng :
-9.223.370.036.854.775.808 đến
9.223.372.036.854.775.807
ulong 8 Uint64 Số nguyên không dấu từ 0 đến 0xffffffffffffffff
• Chuyển đổi các kiểu dữ liệu
Những đối tượng của một kiểu dữ liệu này có thể được chuyển sang những đối tượng
của một kiểu dữ liệu khác thông qua cơ chế chuyển đổi tường minh hay ngầm định.
Chuyển đổi nhầm định được thực hiện một cách tự động, trình biên dịch sẽ thực hiện
công việc này. Còn chuyển đổi tường minh diễn ra khi chúng ta gán ép một giá trị cho
kiểu dữ liệu khác.
1.3.2.2 . Biến và hằng
• Biến
Một biến là một vùng lưu trữ với một kiểu dữ liệu. Biến có thể được gán giá trị và
cũng có thể thay đổi giá trị khi thực hiện các lệnh trong chương trình.
Để tạo một biến chúng ta phải khai báo kiểu của biến và gán cho biến một tên duy
nhất. Biến có thể được khởi tạo giá trị ngay khi được khai báo, hay nó cũng có thể được
gán một giá trị mới vào bất cứ lúc nào trong chương trình.
Cú pháp C# sau đây để khai báo một biến :
[ từ khóa ] <kiểu dữ liệu> <tên biến> ;
VD: private int x;
• Hằng
Hằng cũng là một biến nhưng giá trị của hằng không thay đổi. Biến là công cụ rất
mạnh, tuy nhiên khi làm việc với một giá trị được định nghĩa là không thay đổi, ta phải
đảm bảo giá trị của nó không được thay đổi trong suốt chương trình. Để ngăn ngừa việc
gán giá trị khác, ta phải sử dụng biến kiểu hằng.
Cú pháp C# sau đây để khai báo một hằng :
[ từ khóa ] <const> <kiểu dữ liệu> < tên hằng>=<giá trị> ;
1.3.2.3 . Câu lệnh
Trong C# một chỉ dẫn lập trình đầy đủ được gọi là câu lệnh. Chương trình bao gồm
nhiều câu lệnh tuần tự với nhau. Mỗi câu lệnh phải kết thúc với một dấu chấm phẩy, ví
dụ như:
int x; // một câu lệnh
x = 32; // câu lệnh khác
int y =x; // đây cũng là một câu lệnh
Những câu lệnh này sẽ được xử lý theo thứ tự. Đầu tiên trình biên dịch bắt đầu ở vị
trí đầu của danh sách các câu lệnh và lần lượt đi từng câu lệnh cho đến lệnh cuối cùng,
tuy nhiên chỉ đúng cho trường hợp các câu lệnh tuần tự không phân nhánh.
Có hai loại câu lệnh phân nhánh trong C# là : phân nhánh không có điều kiện
(unconditional branching statement) và phân nhánh có điều kiện (conditional branching
statement).
Ngoài ra còn có các câu lệnh làm cho một số đoạn chương trình được thực hiện nhiều
lần, các câu lệnh này được gọi là câu lệnh lặp hay vòng lặp. Bao gồm các lệnh
lặp for, while, …
• Phân nhánh không có điều kiện
Phân nhánh không có điều kiện có thể tạo ra bằng hai cách: gọi một hàm và dùng từ
khoá phân nhánh không điều kiện.
- Gọi hàm
Khi trình biên dịch xử lý đến tên của một hàm, thì sẽ ngưng thực hiện hàm hiện thời
mà bắt đầu phân nhánh dể tạo một gọi hàm mới. Sau khi hàm vừa tạo thực hiện xong và
trả về một giá trị thì trình biên dịch sẽ tiếp tục thực hiện dòng lệnh tiếp sau của hàm ban
đầu.
- Từ khoá phân nhánh không điều kiện
Để thực hiện phân nhánh ta gọi một trong các từ khóa sau : goto ,break , continue,
……
• Phân nhánh có điều kiện
Phân nhánh có điều kiện được tạo bởi các lệnh điều kiện. Các từ khóa của các lệnh
này như : if, else, switch. Sự phân nhánh chỉ được thực hiện khi biểu thức điều kiện phân
nhánh được xác định là đúng.
- Câu lệnh if else
Câu lệnh phân nhánh if else dựa trên một điều kiện. Điều kiện là một biểu thức sẽ
được kiểm tra giá trị ngay khi bắt đầu gặp câu lệnh đó. Nếu điều kiện được kiểm tra là
đúng, thì câu lệnh hay một khối các câu lệnh bên trong thân của câu lệnh if được thực
hiện.
Trong câu điều kiện if else thì else là phần tùy chọn. Các câu lệnh bên trong thân
của else chỉ được thực hiện khi điều kiện của if là sai. Do vậy khi câu lệnh đầy
đủ if else được dùng thì chỉ có một trong hai if hoặc else được thực hiện. Ta có cú pháp
câu điều kiện if….else
if (biểu thức điều kiện) <Khối lệnh thực hiện khi điều kiện đúng>
[else <Khối lệnh thực hiện khi điều kiện sai>]
Nếu các câu lệnh trong thân của if hay else mà lớn hơn một lệnh thì các lệnh này phải
được bao trong một khối lệnh, tức là phải nằm trong dấu khối { }
- Câu lệnh switch
Khi có quá nhiều điều kiện để chọn thực hiện thì dùng câu lệnh if sẽ rất rối rắm và
dài dòng, Các ngôn ngữ lập trình cấp cao đều cung cấp một dạng câu lệnh switch liệt kê
các giá trị và chỉ thực hiện các giá trị thích hợp. C# cũng cung cấp câu lệnh
nhảyswitch có cú pháp sau:
switch (biểu thức điều kiện)
{
case <giá trị>: <Các câu lệnh thực hiện><lệnh nhảy>
[default: <Các câu lệnh thực hiện mặc định>]
}
Cũng tương tự như câu lệnh if, biểu thức để so sánh được đặt sau từ khóa switch, tuy
nhiên giá trị so sánh lại được đặt sau mỗi các từ khóa case. Giá trị sau từ khóa case là các
giá trị hằng số nguyên
Nếu một câu lệnh case được thích hợp tức là giá trị sau case bằng với giá trị của biểu
thức sau switch thì các câu lệnh liên quan đến câu lệnh case này sẽ được thực thi. Tuy
nhiên phải có một câu lệnh nhảy như break, goto để điều khiển nhảy qua các casekhác.Vì
nếu không có các lệnh nhảy này thì khi đó chương trình sẽ thực hiện tất cả các case theo
sau.
• Các câu lệnh lặp
C# cung cấp một bộ mở rộng các câu lệnh lặp, bao gồm các câu lệnh
lặp for, while và do while. Ngoài ra ngôn ngữ C# còn bổ sung thêm một câu lệnh
lặp foreach, lệnh này mới đối với người lập trình C/C++ nhưng khá thân thiện với người
lập trình VB. Cuối cùng là các câu lệnh nhảy như goto, break, continue, và return.
- Vòng lặp while
Ý nghĩa của vòng lặp while là: “Trong khi điều kiện đúng thì thực hiện các công việc
này”.
Cú pháp sử dụng vòng lặp while như sau:
while (Biểu thức)
<Câu lệnh thực hiện>
Biểu thức của vòng lặp while là điều kiện để các lệnh được thực hiện, biểu thức này
bắt buộc phải trả về một giá trị kiểu bool là true/false. Nếu có nhiều câu lệnh cần được
thực hiện trong vòng lặp while thì phải đặt các lệnh này trong khối lệnh.
- Vòng lặp for
+ Vòng lặp for bao gồm ba phần chính:
+ Khởi tạo biến đếm vòng lặp
+ Kiểm tra điều kiện biến đếm, nếu đúng thì sẽ thực hiện các lệnh bên trong vòng for
+ Thay đổi bước lặp.
+ Cú pháp sử dụng vòng lặp for như sau:
for ([ phần khởi tạo] ; [biểu thức điều kiện]; [bước lặp])
<Câu lệnh thực hiện>
- Câu lệnh nhảy break và continue
Khi đang thực hiện các lệnh trong vòng lặp, có yêu cầu như sau: không thực hiện các
lệnh còn lại nữa mà thoát khỏi vòng lặp, hay không thực hiện các công việc còn lại của
vòng lặp hiện tại mà nhảy qua vòng lặp tiếp theo. Để đáp ứng yêu cầu trên C# cung cấp
hai lệnh nhảy là break và continue để thoát khỏi vòng lặp.
Break khi được sử dụng sẽ đưa chương trình thoát khỏi vòng lặp và tiếp tục thực hiện
các lệnh tiếp ngay sau vòng lặp.
Continue ngừng thực hiện các công việc còn lại của vòng lặp hiện thời và quay về
đầu vòng lặp để thực hiện bước lặp tiếp theo
Hai lệnh break và continue tạo ra nhiều điểm thoát và làm cho chương trình khó hiểu
cũng như là khó duy trì. Do vậy phải cẩn trọng khi sử dụng các lệnh nhảy này.
1.3.2.4 . Toán tử
Toán tử được kí hiệu bằng một biểu tượng dùng để thực hiện một hành động. Các
kiểu dữ liệu cơ bản của C# như kiểu nguyên hỗ trợ rất nhiều các toán tử như toán tử gán,
toán tử toán học, logic
• Toán tử gán
Đến lúc này toán tử gán khá quen thuộc với chúng ta, hầu hết các chương trình minh
họa từ đầu sách đều đã sử dụng phép gán. Toán tử gán hay phép gán làm cho toán hạng
bên trái thay đổi giá trị bằng với giá trị của toán hạng bên phải. Toán tử gán là toán tử hai
ngôi.
Đây là toán tử đơn giản nhất thông dụng nhất và cũng dễ sử dụng nhất.
• Toán tử toán học
Các phép toán số học cơ bản (+,-,*,/)
Các phép toán này không thể thiếu trong bất cứ ngôn ngữ lập trình nào, C# cũng
không ngoại lệ, các phép toán số học đơn giản nhưng rất cần thiết bao gồm: phép cộng
(+), phép trừ (-), phép nhân (*), phép chia (/) nguyên và không nguyên.
Phép toán chia lấy dư
Để tìm phần dư của phép chia nguyên, chúng ta sử dụng toán tử chia lấy dư (%). Ví
dụ, câu lệnh sau 8%3 thì kết quả trả về là 2 (đây là phần dư còn lại của phép chia
nguyên).
• Toán tử tăng và giảm
Toán tử Ý nghĩa
+=
Cộng thêm giá trị toán hạng bên phải vào giá trị toán hạng bên
trái
-=
Toán hạng bên trái được trừ bớt đi một lượng bằng giá trị của
toán hạng bên phải
*=
Toán hạng bên trái được nhân với một lượng bằng giá trị của
toán hạng bên phải.
/=
Toán hạng bên trái được chia với một lượng bằng giá trị của
toán hạng bên phải.
%=
Toán hạng bên trái được chia lấy dư với một lượng bằng giá trị
của toán hạng bên phải.
Mô tả các phép toán tự gán
• Toán tử logic
Toán tử Ký hiệu Biểu thức logic Giá trị Logic
and && (x == 3) && (y ==7) false
Cả hai điều kiện phải
đúng
or || (x == 3) || (y == 7) true
Chỉ cần một điều
kiện đúng
not ! ! (x == 3 ) true
Biểu thức trong
ngoặc phải sai
Các toán tử logic (giả sử x = 5, y = 7).
• Toán tử quan hệ
Tên toán tử Kí hiệu Biểu thức so sánh Kết quả so sánh
So sánh bằng ==
value1 == 100
value1 == 50
true
false
Không bằng !=
value2 != 100
value2 != 90
false
true
Lớn hơn >
value1 > value2
value2 > value1
True
false
Lớn hơn hay bằng >= value2 >= 50 true
Nhỏ hơn <
value1 < value2
value2 < value1
False
true
Nhỏ hơn hay bằng <= value1 <= value2 false
Các toán tử so sánh (giả sử value1 = 100, và value2 = 50).
1.4 . Lớp, đối tượng
• Định nghĩa lớp
Để định nghĩa một kiểu dữ liệu mới hay một lớp đầu tiên phải khai báo rồi sau đó
mới định nghĩa các thuộc tính và phương thức của kiểu dữ liệu đó. Khai báo một lớp
bằng cách sử dụng từ khoá class. Cú pháp đầy đủ của khai báo một lớp như sau:
[Thuộc tính] [Bổ sung truy cập] class <Định danh lớp> [: Lớp cơ sở]
{
<Phần than lớp: bao gồm định nghĩa các thuộc tính và phương thức hành động >
}
• Thuộc tính truy cập
Thuộc tính truy cập quyết định khả năng các phương thức của lớp bao gồm việc các
phương thức của lớp khác có thể nhìn thấy và sử dụng các biến thành viên hay những
phương thức bên trong lớp
Giới hạn truy cập
public Không hạn chế. Những thành viên được đánh dấu public có thể được dùng
bởi bất kì các phương thức của lớp bao gồm những lớp khác.
private Thành viên trong một lớp A được đánh dấu là private thì chỉ được truy
cập bởi các phương thức của lớp A.
protected Thành viên trong lớp A được đánh dấu là protected thì chỉ được các
phương thức bên trong lớp A và những phương thức dẫn xuất từ lớp A
truy cập.
internal Thành viên trong lớp A được đánh dấu là internal thì được truy cập bởi
những phương thức của bất cứ lớp nào trong cùng khối hợp ngữ với A.
protected
internal
Thành viên trong lớp A được đánh dấu là protected internal được truy cập
bởi các phương thức của lớp A, các phương thức của lớp dẫn xuất của A,
và bất cứ lớp nào trong cùng khối hợp ngữ của A.
1.4.1 Cấu trúc chương trình của C#
+ C# được xem như một ngôn ngữ lập trình thay thế C++, vì nó cung cấp hầu hết tính
năng mạnh mà trước đây chỉ có trong C++.
+ Cấu trúc của ngôn ngữ lập trình C# như sau:
// vùng bắt đầu khai báo sử dụng không gian tên
using System;
using System.Collections.Generic;
using System.Text;
// vùng kết thúc khai báo sử dụng không gian tên
// khai báo không gian tên của ứng dụng
namespace myconsoleApplication
{// vùng bắt đầu khai báo tên các class
Class program
{
// vùng bắt đầu khai báo tên các phương thức trong lớp
Static void Main (String [ ] args )
{
// Vùng khai báo lệnh
}
}
}
CHƯƠNG 2:
LÝ THUYẾT VỀ THUẬT TOÁN TÌM ĐƯỜNG ĐI NGẮN NHẤT
2.1. Các khái niệm cơ bản của lý thuyết đồ thị
2.1.1. Định nghĩa đồ thị
Đồ thị là một cấu trúc rời rạc bao gồm các đỉnh và các cạnh nối các đỉnh này.Chúng
ta phân biệt các loại đồ thị khác nhau bởi kiểu và số lượng cạnh nối hai đỉnh nào đó của
đồ thị . Để có thể hình dung được tại sao lại cần đến các loại đồ thị khác nhau, chúng ta
sẽ nêu ví dụ sử dụng chúng để mô tả một mạng máy tính. Giả sử ta có một mạng gồm
các máy tính và các kênh điện thoại ( gọi tắt là tên thoại ) nối các máy tính này. Chúng ta
có thể biểu diễn các vị trí đặt máy tính bởi các điểm và các kênh thoại nối chúng bởi các
đoạn nối như hình 1 dưới đây:
Hà Tây Đồng Nai Huế An Giang
Hà Nội Bình Định
TPHCM
Quãng Ngãi Phú Yên Khánh Hòa
Hình 1.Sơ đồ mạng máy tính
Nhận thấy rằng trong mạng hình 1, giữa hai máy tính bất kỳ chỉ cho phép nhiều nhất
là một kênh thoại nối chúng,kênh thoại này cho phép liên lạc cả hai chiều và không có
máy tính nào lại được nối với chính nó. Sơ đồ mạng máy tính cho trong hình 1 được gọi
là đơn đồ thị vô hướng => ta đi đến định nghĩa sau:
Định nghĩa 1. Đơn đồ thị vô hướng G=(V,E) bao gồm V là tập đỉnh, và E là tập các
cặp không có thứ tự gồm hai phần tử khác nhau của V gọi là các cạnh.
Trong trường hợp giữa hai máy tính nào đó thường xuyên phải truyền tải nhiều
thông tin người ta phải nối hai máy này bởi nhiều kênh thoại. Mạng với đa kênh thoại
giữa các máy tính được cho trong hình 2.
Hà Tây Đồng Nai Huế An Giang
Hà Nội
TPHCM Bình Định
Quãng Ngãi
Khánh Hòa
Phú Yên
Hình 2. Sơ đồ mạng máy tính với đa kênh thoại
Định nghĩa 2. Đa đồ thị vô hướng G=(V,E) bao gồm V là tập các đỉnh, và E là họ
các cặp không có thứ tự gồm hai phần tử khác nhau của V gọi là các cạnh. Hai cạnh e
1
va e
2
được gọi là cạnh lặp nếu chúng cùng tương ứng với một cặp đỉnh.
Hà Tây Đồng Nai Huế An Giang
Hà Nội TPHCM Bình Định
Quãng Ngãi Phú Yên
Khánh Hòa
Hình 3. Sơ đồ mạng máy tính với kênh thông báo.
Rõ ràng mỗi đơn đồ thị đều là đa đồ thị, nhưng không phải đa đồ thị nào cũng là đơn
đồ thị, vì trong đa đồ thị có hai hay nhiều hơn cạnh nối một cặp đỉnh nào đó.
Trong mạng máy tính có thể có những kênh thoại nối một máy tính nào đó với chính
nó ( chẳng hạn với mục đích thông báo). Mạng như vậy được cho trong hình 3. Như vậy
đa đồ thị không thể mô tả được mạng như vậy, bởi vì có những khuyên (cạnh nối một
đỉnh vói chính nó). Trong trường hợp này chúng ta cần sử dụng đến khái niệm giả đồ thị
vô hướng, được định nghĩa như sau:
Định nghĩa 3. Giả đồ thị vô hướng G=(V,E) bao gồm V là tập các đỉnh, và E là họ
các cặp không có thứ tự gồm hai phần tử ( không nhất thiết phải khác nhau ) của V gọi
là các cạnh.Cạnh e được gọi là khuyến nếu có dạng e=(u,u).
Các kênh thoại trong mạng máy tính có thể chỉ cho phép truyền tin theo một
chiều.Chẳng hạn trong hình 4 máy chủ ở Hà Nội chỉ có thể nhận tin từ các máy ở địa
phương, có một số máy chỉ có thể gửi tin đi ,còn các kênh thoại cho phép truyền tin theo
cả hai chiều được thay thế bởi hai cạnh có hướng ngược chiều nhau.
Hà Tây Đồng Nai Huế An Giang
Hà Nội TPHCM Bình Định
Phú Yên
Quảng ngãi Khánh Hòa
Hình 4. Mạng máy tính với các kênh thoại một chiều
Ta đi đến định nghĩa sau:
Định nghĩa 4. Đơn đồ thị có hướng G=(V,E)bao gồm V là tập các đỉnh, và E là tập
các cặp có thứ tự gồm hai phần tử khác nhau của V gọi là các cung.
Nếu trong mạng có thể có đa kênh thoại một chiều, ta sẽ phải sử dụng đến khái niệm đa
đồ thị có hướng:
Định nghĩa 5. Đa đồ thị có hướngG=(V,E) bao gồm V là tập các đỉnh, và E là họ
các cặp có thứ tự gồm hai phần tử khác nhau của V gọi là các cung.Hai cung e
1
va e
2
tương ứng với cùng một cặp đỉnh được gọi là cung lặp.
Trong các phần tiếp theo chủ yếu chúng ta sẽ làm việc với đơn đồ thị vô hướng và
đơn đồ thị có hướng. Vì vậy, để cho ngắn gọn, ta sẽ bỏ qua tính từ đơn mỗi khi nhắc đến
chúng.
2.1.2. Bậc của đỉnh
Trong mục này chúng ta sẽ trình bày một số thuật ngữ cơ bản của lý thuyết đồ thị.
Trước tiên, ta xét các thuật ngữ mô tả các đỉnh và cạnh của đồ thị vô hướng.
Định nghĩa 1. Hai đỉnh u và v của đồ thị có hướng G được gọi là kề nhau nếu (u,v)
là cạnh của đồ thị G. Nếu e=(u,v) là cạnh của đồ thị thì ta nói cạnh này là cạnh liên
thuộc với hai đỉnh u và v, hoặc cũng nói là cạnh e nối đỉnh u và đỉnh v, đồng thời các
đỉnh u và v sẽ được gọi là các đỉnh đầu của cạnh (u,v).
Để có thể biết có bao nhiêu cạnh liên thuộc với một đỉnh, ta đưa vào định nghĩa sau :
Định nghĩa 2. Ta gọi bậc của đỉnh v trong đồ thị vô hướng là số cạnh liên thuộc với
nó ta sẽ kí hiệu là deg(v).
b c d
a f e g
2.1.3. Định nghĩa đường đi, chu trình , đồ thị liên thông.
Định nghĩa 1:Đường đi độ dài n từ đỉnh u đến đỉnh v, trong đó n là số nguyên
dương, trên đồ thị vô hướng G=(V,E) là dãy
x
o
, x
1
, , x
n-1
, x
n
trong đó u=x
0
, v=x
n
, ( x
i
, x
i+1
)
∈
E , i= 0, 1, 2 , , n-1.
Đường đi nói trên còn có thể biểu diễn dưới dạng các cạnh:
(x
0
, x
1
) , ( x
1
, x
2
), , ( x
n-1
, x
n
).
Đỉnh u gọi là đỉnh đầu, còn đỉnh v gọi là đỉnh cuối của đường đi. Đường đi có đỉnh
đầu trùng với đỉnh cuối ( tức là u=v) được gọi là chu trình. Đường đi hay chu trình
được gọi là đơn nếu như không có cạnh nào bị lặp lại.
Thí dụ 1. Trên đồ thị vô hướng cho trong hình 1: a,d,c,f,e là đường đi đơn độ dài 4.
còn d,e,c,a không là đường đi do (e,c) không phải là cạnh của đồ thị. Dãy b,c,f,e,b là chu
trình độ dài 4. Đường đi a,b,e,d,a,b có độ dài là 5 không phải là đường đi đơn, do cạnh
(a,b) có mặt trong nó hai lần.
a b c a b c
d e f d e f
Hình 1. Đường đi trên đồ thị
Khái niệm đường đi và chu trình trên đồ thị có hướng được định nghĩa hoàn toàn
tương tự như trường hợp đồ thị vô hướng, chỉ khác là ta chú ý đến hướng trên các cung.
Định nghĩa 2. Đường đi độ dài n từ đỉnh u đến đỉnh v, trong đó n là số nguyên
dương, trên đồ thị có hướng G=(V,A) là dãy
x
o
, x
1
, , x
n-1
, x
n
trong đó u=x
0
, v=x
n
, ( x
i
, x
i+1
)
∈
A , i= 0, 1, 2 , , n-1.
Đường đi nói trên còn có thể biểu diễn dưới dạng các cung:
(x
0
, x
1
) , ( x
1
, x
2
), , ( x
n-1
, x
n
).
Đỉnh u gọi là đỉnh đầu, còn đỉnh v gọi là đỉnh cuối của đường đi. Đường đi có đỉnh
đầu trùng với đỉnh cuối ( tức là u=v) được gọi là chu trình. Đường đi hay chu trình
được gọi là đơn nếu như không có cung nào bị lặp lại.
Thí dụ 2. Trên đồ thị có hướng cho trong hình 1: a,d,c,f,e là đường đi đơn độ dài 4.
Còn d,e,c,a không là đường đi do (e,c) không phải là cung của đồ thị. Dãy b,c,f,e,b là chu
trình độ dài 4. Đường đi a,b,e,d,a,b có độ dài là 5 không phải là đường đi đơn, do cung
(a,b) có mặt trong nó hai lần.
Xét một mạng máy tính .Một câu hỏi đặt ra là hai máy tính bất kỳ trong mạng này
có thể trao đổi được thông tin với nhau hoặc trực tiếp qua kênh nối chúng hợăc thông
qua một hoặc vài máy tính trung gian trong mạng? Nếu sử dụng đồ thị để biểu diễn mạng
máy tính này (trong đó các đỉnh của đồ thị tương ứng với các máy tính , còn các cạnh
tương ứng với các kênh nối) câu hỏi đó được phát biểu trong ngôn ngữ đồ thị như sau:
Tồn tại hay chăng đường đi giữa m.ọi cặp đỉnh của đồ thị ?
Định nghĩa 3. Đồ thị vô hướng G=(V,E) được gọi là liên thông nếu luôn tìm được
đường đi giữa hai đỉnh bất kỳ của nó.
Như vậy hai máy tính bất kỳ trong mạng có thể trao đổi thông tin đượcvới nhau khi
và chỉ khi đồ thị tương ứng với mạng này là đồ thị liên thông.
Thí dụ 3. Trong hình 2: Đồ thị G là liên thông, đồ thị H là không liên thông
a b
H
1
c
d e
H
2
g f
H
3
G H
Hình 2. Đồ thị liên thông G và đồ thị H gồm 3 thành phần liên thông H
1
,H
2
,H
3
.
Định nghĩa 4. Ta gọi đồ thị con của đồ thị G=(V,E) là đồ thị H=(W,F), trong đó W
⊆
V và F
⊆
E
Trong trường hợp đồ thị là không liên thông , nó sẽ rã ra thành một số đồ thị con liên
thông đôi một không có đỉnh chung. Những đồ thị con liên thông như vậy ta sẽ gọi là các
thành phần liên thông của đồ thị.
Thí dụ 4. Đồ thị H trong hình 2 gồm 3 thành phần liên thông là H
1
,H
2
,H
3.
Trong mạng máy tính có thể có những máy ( những kênh nối ) mà sự hỏng hóc của
nó có thể ảnh hưởng đến việc trao đổi thông tin trong mạng. Các khái niệm tương ứng
với tình huống này được đưa ra trong định nghĩa sau.
Định nghĩa 5. Đỉnh v được gọi là đỉnh rẽ nhánh nếu việc loại bỏ v cùng với các
cạnh liên thuộc với nó khỏi đồ thị làm tăng số thành phần liên thông của đồ thị. Cạnh e
được gọi là cầu nếu việc loại bỏ nó khỏi đồ thị làm tăng số thành phần liên thông của đồ
thị .
Thí dụ 5. trong đồ thị G ở hình 2, đỉnh d và e là đỉnh rẽ nhánh, còn các cạnh (d,g) và
(e,f) là cầu.
Đối với đồ thị có hướng có hai khái niệm liên thông phụ thuộc vào việc ta có xét
đến hướng trên các cung hay không.
Định nghĩa 6. Đồ thị có hướng G=(V,A) được gọi là liên thông mạnh nếu luôn tìm
được đường đi giữa hai đỉnh bất kỳ của nó.
Định nghĩa 7. Đồ thị có hướng G=(V,A) được gọi là liên thông yếu nếu đồ thị vô
hướng tương ứng với nó là đồ thị vô hướng liên thông.
Rõ ràng nếu đồ thị là liên thông mạnh thì nó cũng là liên thông yếu, nhưng điều
ngược lại là không luôn đúng , như chỉ ra trong thí dụ dưới đây.
Thí dụ 6. Trong hình 3 đồ thị G là liên thông mạnh, còn H là liên thông yếu nhưng
không là liên thông mạnh
a b a b
e e
c d
c d
Hình 3. Đồ thị liên thông mạnh G
Đồ thị liên thông yếu H
Một câu hỏi đặt ra là khi nào có thể định hướng các cạnh của một đồ thị vô hướng
liên thông để có thể thu được một đồ thị có hướng liên thông mạnh? Ta sẽ gọi đồ thị như
vậy là đồ thị định hướng được. Định lý dưới đây cho ta tiêu chuẩn nhận biết một đồ thị
có là định hướng được hay không.
Định lý 1. Đồ thị vô hướng liên thông là định hướng được khi và chỉ khi mỗi cạnh
của nó nằm trên ít nhất một chu trình.
Chứng minh. Điều kiện cần. Giả sử (u,v) là một cạnh của đồ thị, từ sự tồn tại đường
đi có hướng từ u đến v và ngược lại suy ra (u,v) phải nằm trên ít nhất một chu trình.
Điều kiện đủ. Thủ tục sau đây cho phép định hướng các cạnh của đồ thị để thu được
đồ thị có hướng liên thông mạnh.Giả sử C là một chu trình nào đó trong đồ thị. Định
hướng các cạnh trên chu trình này theo một hướng đi vòng theo nó. Nếu tất các cạnh của
đồ thị là đã được định hướng thì kết thúc thủ tục. Ngược lại, chính C là một cạnh chưa
định hướng có chung đỉnh với ít nhất một trong số các cạnh đã định hướng. Theo giả
thiết tìm được chu trình C chứa cạnh e. Định hướng các cạnh chưa được định hướng của
C’ theo một hướng dọc theo chu trình này( không định hướng lại các cạnh đã có hướng).
Thủ tục trên sẽ được lặp lại cho đến khi tất cả các cạnh của đồ thị được định hướng. Khi
đó ta thu được đồ thị có hướng liên thông mạnh.
2.2 Các khái niệm mở đầu về đề tài
2.2.1 Mở đầu
Trong phần này chúng ta chỉ xét đồ thị có hướng G=(V,E) và |V|=n,|E|=m với các
cung được gán trọng số, nghĩa là , mỗi cung (u,v)
∈
E của nó được đặt tương ứng với một
số thực a(u,v) gọi là trọng số của nó.Chúng ta sẽ đặt a(u,v)=
∞
, nếu (u,v)
∉
E . Nếu dãy
v
0
, v
1
, , v
p
là một đường đi trên G, thì độ dài của nó được định nghĩa là tổng sau:
p
∑a(v
i-1
, v
i
)
i=1
tức là , độ dài của đường đi chính là tổng
các trọng số trên các cung của nó.(Chú ý rằng
nếu chúng ta gán trọng số cho tất cả các cung đều bằng 1, thì ta thu được định nghĩa độ
dài đuờng đi như là số cung của đường đi.
Bài toán tìm đường đi ngắn nhất trên đồ thị dưới dạng tổng quát có thể được phát
biểu dưới dạng tổng quát như sau : Tìm đường đi có độ dài nhỏ nhất từ một đỉnh xuất
phát s
∈
V đến đỉnh cuối (đích) t
∈
V. Đường đi như vậy sẽ gọi là đường đi ngắn nhất từ
s đến t còn độ dài của nó sẽ kí hiệu là d(s,t) và còn gọi là khoảng cách từ s đến t