You are on page 1of 15

Họ tên: Ngô Thị Diệu Linh

Msv: 17000894
Môn: Thiết kế đánh giá thuật toán

BÀI TẬP TUẦN 4


Phương pháp thử sai (Quay lui, Nhánh cận)

1. Viết chương trình cho các thuật toán đã phân tích và xây dựng (theo bài giảng):
(Chọn 2 trong 3 bài để thực hiện)

Đề bài: Liệt kê các dãy nhị phân có độ dài n.


 Viết chương trình:
package week4;

import java.util.Scanner;

public class Binary_Bai1 {


static int x[],n;
public static void main(String args[]) {
Scanner sc=new Scanner(System.in);
System.out.println("nhap n");
n=sc.nextInt();
x=new int[n];
Binary(0);
}
public static void Binary(int i) {
for(int j=0;j<=1;j++) {
x[i]=j; if(i==n-
1) {
print();
}
else
Binary(i+1);
}

}
public static void print() {
for(int i=0;i<n;i++) {
System.out.print(x[i]);
}
System.out.println();
}
}
Đề bài: Bài toán người bán hàng: Một người bán hàng trên hệ thống n thành phố. Giữa
các thành phố có thể có hoặc không các đường nối, mỗi đường nối có chi phí xác định từ
trước. Người bán hàng xuất phát từ một thành phố, đi tới tất cả các thành phố khác và
mỗi thành phố đi qua lần và quay trở lại thành phố ban đầu. Hãy xác định một hành trình
sao cho tổng chi phí trên đường đi là nhỏ nhất.
 Viết chương trình
package week4;

import java.util.Scanner;

class TSP_Bai1 {

private static int[][] c; // matran chi phi


private static int cMin; // min cua ma tran c
private static int[] flag; // mang danh dau xem thanh pho nao da di qua roi
private static int[] x; // mang luu phuong an hien tai
private static int[] x_best; // mang luu phuong an tot nhat
private static int f = Integer.MAX_VALUE; // vo cung
private static int sum = 0, cost = 0, g = 0;

public TSP_Bai1(int[][] c) {
this.c = c;
flag = new int[c.length];
x = new int[c.length];
x_best = new int[c.length];

// tim Cmin
public static int cmin() {
cMin = c[0][0];
for (int i = 0; i < c.length; i++) {
for (int j = 0; j < c.length; j++) {
if (cMin > c[i][j])
cMin = c[i][j];
}
}
return cMin;
}

/* in ra mang x_best */
public static void printResult() {
for (int i = 0; i < c.length; i++) {
System.out.print((x_best[i] + 1) + " ");

public static void update() {


if (cost < f) { // neu chi phi hien tai nho hon chi phi Min
f = cost;
for (int i = 0; i < c.length; i++) {
x_best[i] = x[i];
}
}

/*
* O buoc thu i, nguoi du lich se di qua thanh pho nao
*/
// thuat toan ap dung cho chay tu thanh pho 0 den cac thanh pho con lai
// khong ap dung cho chay tu thanh pho bat ki
public static void tsp(int i) {
for (int j = 1; j < c.length; j++) {
if (flag[j] == 0) {
x[i] = j;
flag[j] = 1;
sum = sum + c[x[i - 1]][x[i]]; //chi phi
g = sum + (c.length - i) * cmin(); // tinh can : chi phi
tu 0 den thanh pho hien tai+ chi phi nho nhat cua cac thanh pho con lai

if (i == c.length - 1) {
cost = sum + c[x[c.length - 1]][0]; // chi phi tu 0
den thanh pho cuoi cung+ chi phi tp cuoi cung + thanh pho ban dau
update();

} else {
if (g < f)
tsp(i + 1);

sum = sum - c[x[i - 1]][x[i]];


flag[j] = 0;
}
}

public static void main(String[] args) {

// nhap ma tran chi phi

// n=sc.nextInt();

int c[][]= { { 100, 3, 14, 18, 15 },


{ 3, 100, 4, 21, 20 },
{ 17, 9, 100, 16, 4 },
{ 6, 2, 7, 100, 12 },
{ 9, 15, 11, 5, 100 } };

TSP_Bai1 tsp = new TSP_Bai1(c);

for (int i = 0; i < c.length; i++) {

flag[i] = 0;

}
System.out.println("bài toán người bán hàng");
System.out.println("phương pháp nhánh cận");
tsp.tsp(1); // tu thanh pho 0 di den thanh pho 1
tsp.printResult();
}
}

2. Phân tích bài toán, xây dựng thuật toán và viết chương trình cho các bài toán
sau:(Chọn 2 trong 4 bài để thực hiện)

Đề bài: 2.1. Liệt kê các hoán vị của n phần tử.


 Phân tích bài toán:
 Mỗi hoán vị từ n phần tử của X có thể biểu diễn bởi bộ có thứ tự n thành
phần: a = (a1, a2,.., an) thoả mãn ai ∈ X, I = 1, 2,.., n, ap≠ aq, p≠ q.
 Trên tập các hoán vị từ n phần tử của X có thể xác định nhiều thứ tự khác
nhau. Tuy nhiên, thứ tự dễ thấy nhất là thứ tự được định nghĩa như sau: Ta
nói hoán vị a = a1a2... an đi trước hoán vị a’ = a1’a2’...an’ trong thứ tự và ký
hiệu là a < a', nếu tìm được chỉ số k (1 ≤ k ≤ n) sao cho: a1 = a1', a2 =
a2',....,ak < ak'.

 Xây dựng thuật toán: Chẳng hạn X = { 1, 2, 3, 4}. Các hoán vị các phần tử của X
được liệt kê theo thứ tự từ điển {1 2 3 4}, {1 2 4 3}, {1 3 2 4}, {1 4 2 3}, {1 4 3
2}, {2 1 3 4}, {2 1 4 3}, {2 3 1 4}, {2 3 4 1}....{4 3 2 1}.
- Như vậy, hoán vị đầu tiên trong thứ tự từ điển là (1, 2, …, n) và hoán vị cuối cùng
là (n, n- 1,..., 1). Giả sử a = a1a2... an là một hoán vị chưa phải là cuối cùng. Khi đó
ta có thể chứng minh được rằng, hoán vị kế tiếp trong thứ tự từ điển có thể xây dựng
bằng cách thực hiện các qui tắc biến đổi sau đối với hoán vị hiện tại:
 Tìm từ phải qua trái hoán vị có chỉ số j đầu tiên thoả mãn aj< aj+1.
 Tìm ak là số nhỏ nhất còn lớn hơn aj trong các số ở bên phải aj;
 Đổi chỗ aj với ak
 Lật ngược đoạn từ aj+1 đến an.

VD: Chẳng hạn ta đang có hoán vị (3, 6, 2, 5, 4, 1), cần xây dựng hoán vị kế tiếp theo thứ
tự

 Ta duyệt từ j = n-1 sang bên trái để tìm j đầu tiên thoả mãn aj < aj+1 ta nhận
được j = 3 ( a3=2 <a 4=5).
 Số nhỏ nhất còn lớn hơn a3 trong các số bên phải a3 là a5(a5=4).
 Đổi chỗ a3 cho a5 ta thu được (3, 6, 4, 5, 2, 1),
 Lật ngược đoạn từ a4 đến a6 ta nhận được (3,6,4,1,2,5).
 Viết chương trình:
package week4;

import java.util.Scanner;

public class HoanVi_bai2_1 {

static int x[],n,b[];


public static void main(String args[]) {
Scanner sc=new Scanner(System.in);
System.out.println("nhap n");
n=sc.nextInt();
x=new int[n]; // luu gia ti
b=new int[n]; // danh dau
for(int i=0;i<n;i++) {
b[i]=0;
}
Binary(0);
}
public static void Binary(int i) {
for(int j=0;j<n;j++) {
if(b[j]==0) {
x[i]=j;
b[j]=1;
if(i==n-1)
print();

else
Binary(i+1);
b[j]=0;
}

}
}
public static void print() {
for(int i=0;i<n;i++) {
System.out.print(x[i]+1);
}
System.out.println();
}
}
Đề bài: 2.4. Bài toán xâu ABC: Cho số nguyên dương N <100, tìm 1 xâu gồm toàn các kí
tự A,B,C thoả mãn: Xâu có độ dài N, 2 đoạn con bất kì liền nhau đều khác nhau, xâu có ít
kí tự C nhất.
Phân tích bài toán:

- Nếu dãy X1 X 2 …X n thỏa mãn 2 đoạn con bất kỳ liền nhau đều khác nhau, thì trong
4 kí tự liên tiếp bất kỳ bao giờ cũng phải có 1 kí tự “C”. Như vậy với 1 dãy con
gồm k kí tự liên tiếp của dãy X thì số kí tự C trong dãy con đó bắt buộc phải ≥ k/
4.
- Tại bước thử chọn Xi , nếu ta đã có Ti kí tự “C” trong đoạn đã chọn từ X1 đến Xi ,
thì cho dù các bước đệ quy có tốt thì số kí tự “C” sẽ phải chọn them bao giờ cũng
≥ (n-i)/4
Xây dựng bài toán:
Input: file văn bản BC.INP chứa số nguyên dương N < 100
Output: file văn bản ABC.OUT ghi xâu tìm được

ABC.INP ABC.OUT
10 ABACABCBAB

“C” Letter
Count: 2

 Viết chương trình:


package week4;
import java.util.Scanner;
class ABC_Bai2_4 {

static int N;
static char X[] = new char[101]; // luu nghiem hien tai
static char Best[] = new char[101]; //luu nghiem tot nhat
static int MinC; // it ki tu c nhat
static int count=0;

public ABC_Bai2_4() {
}
// kiem tra ki tu trong chuoi co giong nhau k
static boolean isSame(int i, int k) {
int kitu=i-k;
for(int l=0;l<k;l++) { // l la do dai cua chuoi ,l=1..n chuoi co do dai 1..n
if(X[i-l]!=X[kitu-l]) // kiem tra tu ki tu cua chuoi co do dai n
return false; // nau khac nhau thi return false
}
return true;
}

static boolean check(int i) {


//
for(int k=1;k<=(i/2);k++) { // k=1..(i/2) la chuoi co do dai tu 1 den i/2
if(isSame(i,k))
return false;
}

return true;
}

static void keepResult() {


if(count<MinC) {
MinC = count;
for (int i = 1; i <= N; i++) {
Best[i] = X[i];
}

}
}

static void attempt(int i) {


for(char j= 'A';j<='C';j++) {
X[i]=j;
if(check(i)) {
if(j=='C')
count++;

if(i==N) {
keepResult();
}
else {
int g=count+ (N-i)/4; // g luu so luong chuoi co so ki tu hien tai + so
ki tu con lai xuat hien ki tu C
if(g<MinC) // neu g < MinC:so luong c it nhat
attempt(i+1);
}
}

}
}

static void printResult() {


for (int i = 1; i <= N; ++i) {
System.out.print(Best[i]);
}

public static void main(String[] args) {

Scanner sc = new Scanner(System.in);


N = sc.nextInt();

MinC = N;
attempt(1);
printResult();
}
}

3. Tự đặt đề bài toán, phân tích bài toán, xây dựng giải thuật, đánh giá độ phức tạp và
viết chương trình để minh họa phương pháp quay lui, phương pháp nhánh cận
(mỗi phương pháp ít nhất 01 bài).

Phương pháp quay lui:


 Đề bài: Liệt kê các chỉnh hợp k phần tử của các số từ 1 đến n.
 Phân tích bài toán: Liệt kê các chỉnh hợp chập k của tập S={1,2,…,n} (tập S có
n phần tử) ta có thể đưa về liệt kê các cấu hình (x1 ,x2 …, xk ), ở đây các xi ∈ S
và đôi một khác nhau
 Xây dựng giải thuật: Như vậy thủ tục Try(i): xét tất cả các khả năng chọn xi sẽ
khử hết các giá trị từ 1 đến n, mà các giá trị này chưa bị các phần tử đứng
trước
chọn. Muốn xem các giá trị nào chưa được chịn ta sử dụng kỹ thuật dùng mảng
đánh dấu:
 Khởi tạo 1 mảng c1,c2,…, cn mang kiểu logic. Ở đây ci cho biết giá trị i
có còn tự do hay đã bị chọn rồi. Ban đầu khởi tạo tất cả các phần tử
mảng c là TRUE có nghĩa là các phần tử từ 1 đén n đều tự do.
 Trước khi gọi đệ quy tìm xi+1 : ta đặt giá trị j vừa gán cho xi thì ta sẽ đặt
giá trị j vừa thử đó thành tự do (cj =TRUE), bởi khi xi đã nhận 1 giá trị
khác rồi thì các phần tử đứng sau: xi+1,xi+2 … hoàn toàn có thể nhận lại
giá trị j đó.

Input: file văn bản ARRANGE.INP chứa 2 số nguyên dương n, k (1 ≤ ≤


≤20) cách nhau ít nhất 1 dấu cách.
Output: file văn bản ARRANGE.OUT ghi các chỉnh hợp chập k của tập
{1,2,…,n}.

ARRANGE.INP ARRANGE.OUT
32 12
13
21
23
31
32

NX: Khi k=n thì đây là chương trình liệt kê hoán vị

 Đánh giá độ phức tạp thuật toán: ( )

 Viết chương trình

package week4;

import java.util.Scanner;

public class PartialPermuatation_Bai3 {


int n,k;
int a[],b[];
public static void main(String args[])
{

PartialPermuatation_Bai3 b=new PartialPermuatation_Bai3();


b.nhap();
b.Binary(0);

}
public void nhap() {
try (Scanner sc = new Scanner(System.in)) {
System.out.println("phương pháp quay lui");
System.out.println("bài toán chỉnh hợp chập k");
System.out.println("Nhap so n");
n=sc.nextInt();
System.out.println("Nhap so k");
k=sc.nextInt();
sc.close();
}
a=new int[k];
b=new int[n];
for(int i=0;i<n;i++) {
b[i]=1;
}

}
public void Binary(int i)
{
for(int j=0;j<n;j++)
{
if(b[j]==1) {
a[i]=j+1;
b[j]=0; // danh dau chi so j co gia tri j+1 da ton tai
if(i==k-1)
{
for(int m=0;m<k;m++)
{
System.out.print(a[m]+" ");
}
System.out.println();
}
else
Binary(i+1);
b[j]=1; // bo danh dau
}
}
}
}
Phương pháp nhánh cận:
 Đề bài: Bài toán xếp balo (dung phương pháp nhánh cân;)
 Phân tích bài toán: Cho một cái balo có thể đựng một trọng lượng W và n
loại đồ vật, mỗi đồ vật i có một trọng lượng g[i] và một giá trị v[i]. Tất cả
các loại đồ vật đều có số lượng không hạn chế. Tìm một cách lựa chọn các
đồ vật đựng vào balo, chọn loại đồ vật nào? Mỗi loại lấy bao nhiêu sao cho
tổng trọng lượng không vượt quá W và tổng giá trị là lớn nhất.
 1.Tính đơn giá (giá cho 1 đơn vị trọng lượng) cho các loại đồ vật
 2. Xét các loại đồ vật theo thứ tự đơn giá từ lớn đến nhỏ
 3. Với mỗi đồ vật được xét sẽ lấy một số lượng tối đa mà trọng
lượng còn lại của balo cho phép
 4. Xác định trọng lượng còn lại của balo và quay lại bước 3 cho đến
khi không còn có thể chọn được đồ vật nào nữa.

 Xây dựng giải thuật:


 Gọi X=(X1,X2,…,Xn ) với Xi là số nguyên không âm, là một phương
án. Xi là số đồ vật thứ i.
 Cần tìm X sao cho:
X 1.g[1] + X 2.g[2] + … +X n.g[n] ≤ W
F(X) = X1.v[1] + X 2.v[2] +…+ X n.v[n] → MAX

 Đánh giá độ phức tạp thuật toán: T(n) = (2 )

 Viết chương trình:


package week4;

import java.util.Scanner;
class Knapsack_Bai3 {
static int w[]; // mang luu trong luong cua cac do vat
static int v[]; // mang luu gia tri cua cac do vat
static int m; // trong luong toi da cua cai tui
static int n; // so luong do vat
static int x[]; // luu nghiem hien tai
static int x_best[]; // luu nghiem tot nhat
static int sum = 0;
static float g;
static float max = 0;
static int weight;

static float dg[];

public Knapsack_Bai3(int[] w, int[] v, int m, int n) {


this.w = w;
this.v = v;
this.n = n;
this.m = m;
x = new int[n];
x_best = new int[n];
}

/* in ra mang x_best */
public static void printResult() {
for (int i = 0; i < n; i++) {
System.out.print(x_best[i] + " ");
}
}

// cap nhat
public void check() {
if (sum > max) { // neu gia tri hien tai lon hon gia tri max
max = sum;
for (int k1 = 0; k1 < n; k1++)
x_best[k1] = x[k1];
}
}

public void knapsack(int i) {


int chay;
chay = (m - weight) / w[i];

for (int j = chay; j >= 0; j--) {

x[i] = j;
sum = sum + j * v[i]; // cap nhat gia tri
weight = weight + j * w[i]; // cap nhat trong luong
if (i == n - 1 || m - weight <= 0) {
check();

else {
g = sum + (m - weight) *(v[i + 1] / w[i + 1]); // sum +
don gia vat ke tiep * trong luong con lai cua tui
if (g > max) // neu gia tri hien tai + gia tri vat ke tiep
> gia tri max hien tai
knapsack(i + 1);
}
sum = sum - j * v[i]; // cap nhat lai gia tri
weight = weight - j * w[i]; // cap nhat lai trong luong

public void sapxep() {


for (int i = 0; i < n - 1; i++) {
for (int j = 1; j < n; ++j) {

if (dg[j - 1] < dg[j]) {


int tl = v[j - 1];
v[j - 1] = v[j];
v[j] = tl;
int gt = w[j - 1];
w[j - 1] = w[j];
w[j] = gt;
float dgt = dg[j - 1];
dg[j - 1] = dg[j];
dg[j] = dgt;
}
}

}
}

public static void main(String[] args) {


Scanner sc = new Scanner(System.in);
Knapsack_Bai3 kp = new Knapsack_Bai3(w, v, m, n);
System.out.println("phuong pháp nhánh cận");
System.out.println("nhap so luong do vat");
n=sc.nextInt();
System.out.println("trong luong toi da cua tui");
m=sc.nextInt();
System.out.println("nhap trong luong do vat ");
w = new int[n];
v = new int[n];
dg = new float[n];

for (int i = 0; i < n; i++) {


w[i] = sc.nextInt();

}
// System.out.println("nhap gia tri do vat ");
for (int j = 0; j < n; j++) {
v[j] = sc.nextInt();

}
// tinh gia tri

for (int j = 0; j < n; j++) {


dg[j] = (float) v[j] / w[j];

kp.sapxep();// printResult();a

kp.knapsack(0);
kp.printResult();

You might also like