Thử lập trình

Liên hệ QC

VetMini

Ăn cùng góc phố
Tham gia
21/12/12
Bài viết
16,947
Được thích
23,329
Lâu quá không thấy ai đố lập trình.
Ăn cắp được đề bài này đem ra thử xem:

Làm cách nào, cho một nguyên không lớn lắm vẽ được một hình vuông bằng số. Số lớn lồng số nhỏ.
Ví dụ ln = 5 thì hình vẽ ra như sau:

1647005348792.png

Xin lỗi tôi không thể diễn giải thêm vì diễn giải nhiều hơn sẽ trở thành gợi ý cách giải.

Quý vị có thể giải bằng công thức hay VBA.

Điều kiện: giả sử cấu hình máy rất siêu và số n không lớn lắm. Tức là quý vị có thể thoải mái về bất cứ giải thuật nào mà quý vị có thể tưởng tượng ra, không nhất thiết phải tối ưu. Nếu bạn nào chậm chân bị người khác đưa ra giải thuật giống mình thì hãy bình tĩnh "think outside the box", tìm giải thuật khác, hoặc tìm cách cải tiến code/công thức kia. Đây là bài toán thu gom các phương pháp tư duy lô gic chứ không phải thách đố tìm giải thuật tối ưu.
Hy vọng là với chi tiết này chúng ta có thể học thêm được vài giải thuật lạ đời.
.
 
Lần chỉnh sửa cuối:
Nếu VBA thì tôi nghĩ ra 3 cách:
- Chạy từ trong ra (hơi khó)
- Chạy từ ngoài vô (dễ hơn)
- Chạy từng hàng ngang (dễ nhất)
 
Xin lỗi, tôi vừa nhận ra cái đề bài trên chỉ nên dùng cho công thức.

Nếu VBA thì bị kẹt chỗ nó liên hệ đến range của bảng tính. Range của bảng tính có thể chạy ngược chạy xuôi. Và như thế thì mất đi cái thú vị của ghi số: lúc vẽ thì từ trái sang phải (hoặc cả dòng như một chuỗi); và xong một dòng mới được sang dòng kế tiếp.

Xin chỉnh lại:
Nếu viết bằng VBA thì cả cái hình vuông được vẽ trong 1 ô (cell), hoặc trên cửa sổ immediate, tuỳ chọn.

1647007905379.png
Chỉnh sửa 12/03/2022: hình trên tôi gõ bị lỗi hơi nhiều. Cột 8 dòng 3 đángb lẽ phải là số 4, thay vì 3. Và cần chèn thêm dòng 544444445 trước dòng cuối.
.
 
Lần chỉnh sửa cuối:
Nếu quanh năm suốt tháng chỉ lặp đi lặp lại vd. Nhập-Xuất-Tồn thì cái Nhập-Xuất-Tồn đó dù làm trong thời gian rảnh, ở bàn nhậu, đâu có phải là "Thư giãn".
 
Sub test(n As Long) Dim ar(): ReDim ar(1 To 2 * n - 1, 1 To 2 * n - 1) Dim r As Long, c As Long, x As Long For x = n To 1 Step -1 For r = n - x + 1 To n + x - 1 For c = n - x + 1 To n + x - 1 ar(r, c) = x Next c Next r Next x End Sub
Em nghĩ là như thế này
 
Em góp cách nghĩ của em:
Mã:
Sub test()
Call ThuLapTrinh(5)
End Sub
Sub ThuLapTrinh(n As Long)
      Dim a As Long, b As Long
      Dim i As Long, j As Long
      Dim Arr
      Range("A1").Resize(1000, 1000).Value = vbNullString
      a = n * 2 - 1
      b = n * 2 + 1
      ReDim Arr(1 To a, 1 To a)
      For i = 1 To n
            For j = 1 To b - (i * 2)
                  Arr(j + (i - 1), i) = n + (1 - i)
                  Arr(i, j + (i - 1)) = n + (1 - i)
                  Arr((n * 2) - i, (n * 2) - j - (i - 1)) = n + (1 - i)
                  Arr((n * 2) - j - (i - 1), (n * 2) - i) = n + (1 - i)
            Next j

      Next i
      Range("A1").Resize(a, a).Value = Arr
End Sub
 
Ở bài #3 tôi đã sửa đề bài. Mục đích chính là để quý vị suy nghĩ thoát ra giải pháp quen thuộc của bảng tính là mảng 2 chiều.
 
Ở bài #3 tôi đã sửa đề bài. Mục đích chính là để quý vị suy nghĩ thoát ra giải pháp quen thuộc của bảng tính là mảng 2 chiều.
Thêm 1 lần chết đau chết đớn. Đúng là không gươm không đao mà!
----------
Bài #5: Ghi nhiều lần đè lên nhau từ ngoài vào trong: Mỗi hình vuông ghi từng dòng và ghi đầy các ô. Hình vuông trong (nhỏ hơn) ghi đè bên trong hình vuông ngoài. Bạn này giỏi suy luận và giỏi về tà đạo :D
Bài #6: Ghi từ ngoài vào trong, không ghi đè. Mỗi vòng hình vuông là 1 số, ghi từ 2 góc đối diện tỏa ra 2 góc còn lại. Chỉ ghi chung quanh, không ghi ruột. Bạn này giỏi toán số cộng trừ :p
 
Dùng công thức thì tôi dùng thế này ở ô A1, kéo xuống dưới, sang ngang

=IF(OR(ROW(1:1)>5*2-1;COLUMN(A:A)>5*2-1);"";5-MIN(ROW(1:1)-1;COLUMN(A:A)-1;5*2-ROW(1:1)-1;5*2-COLUMN(A:A)-1))
Thay số 5 thành số tùy ý.
 
Lần chỉnh sửa cuối:
Ở bài #3 tôi đã sửa đề bài. Mục đích chính là để quý vị suy nghĩ thoát ra giải pháp quen thuộc của bảng tính là mảng 2 chiều.
Em nghĩ thầy đang có sự nhầm lẫn ở đây, ở bài #3 của thầy em thấy cái hình nó hiển thị dãy số không giống với hình bài #1.
 
Gọi n là cạnh hình vuông, a là chỉ số dòng, b là chỉ số cột.
Số tại vị trí (a, b) bằng [Phần nguyên của (n+1)/2] - [Số nhỏ nhất trong (a, b, n+1-a, n+1-b)] + 1
--
Rich (BB code):
A1=INT((9+1)/2)-MIN(ROW(),COLUMN(),9+1-ROW(),9+1-COLUMN())+1
 
Lần chỉnh sửa cuối:
----------
Bài #5: Ghi nhiều lần đè lên nhau từ ngoài vào trong: Mỗi hình vuông ghi từng dòng và ghi đầy các ô. Hình vuông trong (nhỏ hơn) ghi đè bên trong hình vuông ngoài. Bạn này giỏi suy luận và giỏi về tà đạo :D
Bài #6: Ghi từ ngoài vào trong, không ghi đè. Mỗi vòng hình vuông là 1 số, ghi từ 2 góc đối diện tỏa ra 2 góc còn lại. Chỉ ghi chung quanh, không ghi ruột. Bạn này giỏi toán số cộng trừ :p
Tôi giải thích như vầy:
Thuật toán bài #5 là loại thuật toán trâu bò. Cho trị đi từ số n đến 1 (1n đến n cũng đại khái vậy) và tìm đúng vị trí chúng trong mảng, ghi vào.
Thuật toán bài #5 dựa trên đúng lô gic đề bài. Vẽ từng hình vuông theo số, hình vuông số lớn bọc ngoài hình vuong số nhỏ. Lô gic này tôi nêu ra trong đề bài, thực tế đề bài cũng có thể diễn đạt bằng lô gic khác. Làm theo code của bài ấy là từ ngoài vào trong. Cách làm từ trong ra ngoài đại khái cũng vậy thôi.
Cả hai thuật toán trên, vì cần phải nhảy tùm lum cho nên bắt buộc phải dùng mảng 2 chiều.

Như vậy, thuật toán bài #5 đã giải quyết vấn đề 1 và 2 của bài #2. Vấn đề 3, dựng từng hàng ngang thì sao?
@ tác giả bài #3: tiễn điểu kinh cung. Chưa hoàn toàn chết, thuật toán hàng ngang chưa chắc đã cần mảng 2 chiều.

Và như đã nói ở đầu bài, tôi tránh không gợi ý thêm.

@các tác giả công thức bảng tính: cảm ơn các bạn góp sức. Tôi đang nghiên cứu.

Chỉnh sửa:
Tôi lại gõ nhầm.
Câu 3, "Thuật toán bài #5 dựa trên đúng lô gic đề bài" nên đọc là "Thuật toán bài #6 dựa trên đúng lô gic đề bài".
Cảm ơn tác giả bài #15 đã nhắc nhở.
 
Lần chỉnh sửa cuối:
Tôi giải thích như vầy:
Thuật toán bài #5 là loại thuật toán trâu bò. Cho trị đi từ số n đến 1 (1n đến n cũng đại khái vậy) và tìm đúng vị trí chúng trogn mảng, ghi vào.
Thuật toán bài #5 dựa trên đúng lô gic đề bài. Vẽ từng hình vuông theo số, hình vuông số lớn bọc ngoài hình vuong số nhỏ.
---------
@ tác giả bài #3: tiễn điểu kinh cung. Chưa hoàn toàn chết, thuật toán hàng ngang chưa chắc đã cần mảng 2 chiều.
Chữ đỏ chắc là #6?
tác giả #3 là ai nhỉ?
À, hình bài 3 chỉ có 8 dòng, và dòng 3 hơi sai sai
 
Cách làm từ trong ra ngoài đại khái cũng vậy thôi.
Vấn đề 3, dựng từng hàng ngang thì sao?
Code bài #5 không chạy từ trong ra ngoài được, vì thủ thuật là ghi đè. Code bài #6 thì từ trong ra ngoài được.
Dựng từng hàng ngang thì đang làm dở dang, thấy bài 3 "chỉ nên dùng cho công thức" nên té và chết (1 trong vô số lần).
 
Giải thuật cơ bản: toán Giải Tích.
Ở diễn đàn này có ít nhất là 10 người đủ khả năng toán Giải Tích để làm bài này. Sao chưa thấy ra mặt. Có lẽ do bận.

Tôi chưa xem kỹ bài #9 và #13. Những nếu họ đã viết công thức như vậy thì có lẽ đã biết được hàm số liên hệ giữa toạ độ điểm và trị số ở điểm đó: trị = f(x, y)

Hàm số f được suy ra như sau:
Nếu gọi tâm của hình vuông là điểm mốc (x0, x0) thì trị ở điểm (xi, yi) là MAX(Abs(xi-x0), Abs(yi-y0)) + trị ở điểm mốc (tức là 1). Dùng hàm dịch toạ độ điểm gốc để cộng/trừ độ lệch. Cứ vậy mà tính ra.

Tuy nhiên lập trình thì được hơn công thức ở chỗ có những mẹo nhỏ để áp dụng. Mẹo này là dùng tâm (0,0). Khỏi phải tính độ dịch gốc.

Sub HinhVuong()
' mảng là chỉ để đồng bộ với các bài trên thôi
' nếu in thẳng ra thì không cần mảng
n = 5 ' trị cần vẽ
n = n - 1 ' trung điểm là (0,0) nên các toạ độ là từ 0 đến n-1
Dim a(): ReDim a(-n To n, -n To n)
For i = -n To n
For j = -n To n
a(i, j) = Application.Max(Abs(i), Abs(j)) + 1
Next j
Next i
[a1].Resize(UBound(a) - LBound(a) + 1, UBound(a, 2) - LBound(a, 2) + 1) = a
End Sub

Vẫn còn vài thuật toán hàng ngang khác. Lão đai môn thử xem.
 
Tựa là thử lập trình, nhưng trong mục Thư Giãn, nên cháu cũng thử hàm excel góp vui :D
 

File đính kèm

  • 220312 KimTuThap.xlsx
    11.7 KB · Đọc: 12
...
Dựng từng hàng ngang thì đang làm dở dang, thấy bài 3 "chỉ nên dùng cho công thức" nên té và chết (1 trong vô số lần).
Dựng từng hàng ngang nè. Chú ý là nếu không có điều kiện trái qua phải, trên xuống dưới thì code có thể viết ngắn hơn. Như code bài #18, mảng trong bài này chỉ là để đồng bộ. Chính bản thân giải thuật thì có thể in thẳng ra.

Sub tt()
n = 5
n = n - 1
Dim a(): ReDim a(-n To n, -n To n)
tt2 a, n, n
[a1].Resize(UBound(a) - LBound(a) + 1, UBound(a, 2) - LBound(a, 2) + 1) = a
End Sub

Sub tt2(a(), n, ni)
For i = -n To n
a(-ni, i) = IIf(Abs(i) <= ni, ni, Abs(i)) + 1
Next i
If ni <= 0 Then Exit Sub
tt2 a, n, ni - 1
For i = -n To n
a(ni, i) = a(-ni, i)
Next i
End Sub
 
Web KT
Back
Top Bottom