Ruby, 228 byte * 21895 = 4992060
->n{a=(0..n*2).map{$b=' '*n}
g=0
m=n*2
(n**0.5).to_i.downto(1){|i|n%i<1&&(m=[m,n+h=n/i].min
g+=h+1
g<m+2?(a[g-h-1,1]=(1..h).map{?**i+$b}):(x=(m-h..m).map{|j|r=a[j].rindex(?*);r ?r:0}.max
(m-h+1..m).each{|j|a[j][x+2]=?**i}))}
a}
Beberapa perubahan dari kode ungolfed. Yang terbesar adalah perubahan makna variabel mdari tinggi persegi panjang kuadrat, ke tinggi persegi panjang kuadrat plus n.
Sepele, *40telah diubah *nyang berarti banyak ruang putih yang tidak perlu di sebelah kanan untuk besar n; dan -2diubah menjadi 0yang berarti persegi panjang diplot di bagian bawah selalu kehilangan dua kolom pertama (ini menghasilkan pengepakan yang lebih buruk untuk angka yang hanya faktorisasi (n/2)*2)
Penjelasan
Saya akhirnya menemukan waktu untuk kembali ke ini.
Untuk diberikan n, bidang terkecil harus memiliki ruang yang cukup untuk persegi panjang terpanjang 1*ndan persegi panjang paling langka x*y. Seharusnya jelas bahwa tata letak terbaik dapat dicapai jika kedua persegi panjang memiliki sisi panjangnya yang berorientasi pada arah yang sama.
Mengabaikan persyaratan spasi putih di antara persegi panjang, kami menemukan bahwa total area adalah salah satu (n+y)*x = (n+n/x)*xatau n*(x+1). Either way, ini dievaluasi menjadi n*x + n. Termasuk spasi putih, kita harus memasukkan tambahan xjika kita menempatkan persegi panjang ujung ke ujung, atau njika kita menempatkan persegi panjang berdampingan. Karena itu yang pertama lebih disukai.
Ini memberikan lowerbound berikut (n+y+1)*xuntuk area lapangan:
n area
60 71*6=426
111 149*3=447
230 254*10=2540
400 421*20=8240
480 505*20=10100
Ini menyarankan algoritma berikut:
Find the value (n+y+1) which shall be the field height
Iterate from the squarest rectangle to the longest one
While there is space in the field height, draw each rectangle, one below the other, lined up on the left border.
When there is no more space in the field height, draw the remaining rectangles, one beside the other, along the bottom border, taking care not to overlap any of the rectangles above.
(Expand the field rightwards in the rare cases where this is necessary.)
Sebenarnya mungkin untuk mendapatkan semua persegi panjang untuk kasus uji yang diperlukan dalam batas bawah yang disebutkan di atas, dengan pengecualian 60, yang menghasilkan 71 * 8 = 568 output berikut. Ini dapat ditingkatkan sedikit menjadi 60 * 9 = 540 dengan memindahkan dua persegi panjang tertipis ke kanan menjadi satu kotak lalu naik, tetapi penghematannya minimal, jadi mungkin tidak ada gunanya kode tambahan.
10
12
15
20
30
60
******
******
******
******
******
******
******
******
******
******
***** *
***** *
***** *
***** *
***** *
***** *
***** *
***** *
***** *
***** *
***** *
***** *
*
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
**** *
*
*** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
*** ** *
** *
** *
** *
** *
** *
** *
** *
** *
** *
** *
** *
Ini memberi total area 21895.
Kode tidak dikunci
f=->n{
a=(0..n*2).map{' '*40} #Fill an array with strings of 40 spaces
g=0 #Total height of all rectangles
m=n #Height of squarest rectangle (first guess is n)
(n**0.5).to_i.downto(1){|i|n%i<1&&(puts n/i #iterate through widths. Valid ones have n%i=0. Puts outputs heights for debugging.
m=[m,h=n/i].min #Calculate height of rectangle. On first calculation, m will be set to height of squarest rectangle.
g+=h+1 #Increment g
g<n+m+2? #if the rectangle will fit beneath the last one, against the left margin
(a[g-h-1,1]=(1..h).map{'*'*i+' '*40}) #fill the region of the array with stars
: #else
(x=(n+m-h..n+m).map{|j|r=a[j].rindex('* ');r ?r:-2}.max #find the first clear column
(n+m-h+1..n+m).each{|j|a[j][x+2]='*'*i} #and plot the rectangle along the bottom margin
)
)}
a} #return the array
puts f[gets.to_i]