回 帖 发 新 帖 刷新版面

主题:分正方形问题,我用贪心算法做不出来,请各位帮帮我

【题目】
分正方形问题

有一个M×N(单位:厘米)的长方形(M,N都不大于100),要求把它分成边长是整数厘米的正方形,要求正好分完,并且使分出的正方形个数最少。

我本想用贪心算法做这道题,即每次都分出能分成的最大的正方形,可我发现有些情况就不对。
比如5×6的:
      —— —— —— —— —— —— —— —— —— —— —— ——
     |                                                 |   1×1  |
     |                                                 |         |
                                                        —— ——
     |                                                 |   1×1  |
     |                                                 |         |      
                                                        —— ——
     |                5×5                             |   1×1  |
     |                                                 |         |
                                                        —— ——
     |                                                 |   1×1  |
     |                                                 |         |
                                                        —— ——
     |                                                 |   1×1  |
     |                                                 |         |
      —— —— —— —— —— —— —— —— —— —— —— ——
按我的算法应该分成上图的形状,共6个正方形,可是下图:
      —— —— —— —— —— —— —— —— —— —— —— ——
     |                             |                             |
     |                             |                             |
                                                          
     |           3×3              |         3×3                |
     |                             |                             |
                                              
     |                             |                             |
     |                             |                             |
      —— —— —— —— —— —— —— —— —— —— —— ——                                             
     |                   |                   |                   |
     |                   |                   |                   |  
             2×2                 2×2                2×2
     |                   |                   |                   |
     |                   |                   |                   |
      —— —— —— —— —— —— —— —— —— —— —— ——
却只需要5个正方形。

到底怎么做啊?请各位帮帮我。

回复列表 (共13个回复)

沙发

这道题对于各位来说是非常简单的,
怎么谁都不帮我啊!

要是复赛考到了类似的题,我找你们算账!

板凳

不好意思,我有点笨,还没想到好一点的办法.

3 楼

我想了很久,终于才想到这个办法,实在是有愧于Mato啊,
得数是有了,至于怎么分,呵呵,自己想想.

DefLng A-Z
Print F4F(5,6)

Function F4F(a, b)
If a = 1 Or b = 1 Then
    F4F = a * b
    Exit Function
ElseIf a < b Then
    z = b
    b = a
    a = z
End If
If a Mod b = 0 Then
    F4F = a \ b
    Exit Function
ElseIf a \ b > 1 Then
    F4F = a \ b + F4F(a Mod b, b)
    Exit Function
End If
ss = a * b
For i = b To (b + 1) \ 2 Step -1
    s = 1
    If a <> i Then j1 = i \ (a - i)
    If b <> i Then j2 = i \ (b - i)
    s = s + j1 + j2
    If s < ss Then s = s + Fabcd(a - i, b - i, a - (b - i) * j2, b - (a - i) * j1)
    If s < ss Then ss = s
Next
F4F = ss
End Function
Function Fabcd(a, b, c, d)
If b = 0 Then
    If d > 0 Then Fabcd = F4F(a, d)
ElseIf a = c And b = d And a = b Then
        Fabcd = 1
ElseIf a = c Or b = d Then
        If a < c Then a = c
        If b < d Then b = d
        Fabcd = F4F(a, b)
Else
    If a = b And a = 1 Then
        Fabcd = c + d + 1
    Else
        ss = a * d + b * c - a * b
        If a = d Then
            S1 = 1 + F4F(b, c - a)
        ElseIf a > d Then
            S1 = 1 + Fabcd(a - d, b, c - d, d)
        ElseIf a < d Then
            S1 = 1 + Fabcd(d - a, c - a, b, c)
        End If
        If S1 < ss Then ss = S1
        If b = c Then
            S1 = 1 + F4F(a, d - b)
        ElseIf b > c Then
            S1 = 1 + Fabcd(a, b - c, c, d - c)
        ElseIf b < c Then
            S1 = 1 + Fabcd(d - b, c - b, d, a)
        End If
        If S1 < ss Then ss = S1
        Fabcd = ss
    End If
End If
End Function

4 楼

谢谢moz,你的算法很好,不过我输入11,10它就栈溢出了,这也许就是递归算法的缺陷。

总之,这算法还是很不错的。

5 楼

惭愧惭愧,我再追踪调试一下先.

6 楼

DECLARE FUNCTION KP1& (s$, K&, L&, LL&())
DECLARE FUNCTION Replace1& (s$, A&, N&, M&, L&, X&)
DECLARE SUB Replaces (s$, A$)
DEFLNG A-Z
INPUT "Input  N, M:  ", N, M
s$ = SPACE$(N * M)
IF N < M THEN J = N ELSE J = M
Min = 1 + ABS(M - N) * J
DIM LL(Min)

DO
   i = INSTR(s$, " ")
   IF i = 0 THEN
      IF K < Min THEN
         COLOR , 0
         CLS
         COLOR 7, 1
         FOR zzz = 1 TO LEN(s$) STEP M
             PRINT MID$(s$, zzz, M)
         NEXT
         Min = K
         PRINT K
         'zz2$ = INPUT$(1)
      END IF
      Replaces s$, CHR$(K + 48)
      IF KP1(s$, K, L, LL()) THEN EXIT DO
   ELSE
      K = K + 1
      IF K >= Min THEN
         IF KP1(s$, K, L, LL()) THEN EXIT DO
      ELSE
         L = M - (i - 1) MOD M
         IF L > J THEN L = J
         IF (i + M - 1) \ M + L - 1 > N THEN L = N - ((i + M - 1) \ M - 1)
      END IF
   END IF
   DO UNTIL Replace1(s$, INSTR(s$, " "), N, M, L, K)
      IF L > 1 THEN
         L = L - 1
      ELSE
         IF KP1(s$, K, L, LL()) THEN EXIT DO
      END IF
   LOOP
   LL(K) = L
  'COLOR , 0: CLS : COLOR 7, 1: FOR zzz = 1 TO LEN(s$) STEP M: PRINT MID$(s$, zzz, M): NEXT
  'z2$ = INPUT$(1)
LOOP UNTIL K < 1
PRINT Min

FUNCTION KP1 (s$, K, L, LL())
DO
      K = K - 1
      IF K < 1 THEN EXIT DO
      Replaces s$, CHR$(K + 48)
      L = LL(K) - 1
LOOP UNTIL L > 1
END FUNCTION

FUNCTION Replace1 (s$, A, N, M, L, X)
J = A
FOR i = 1 TO L
    IF MID$(s$, J, L) <> SPACE$(L) THEN
       Replaces s$, STRING$(L, X + 48)
       EXIT FUNCTION
    ELSE
       MID$(s$, J, L) = STRING$(L, X + 48)
    END IF
    J = J + M
NEXT
Replace1 = -1
END FUNCTION

SUB Replaces (s$, A$)
L = LEN(A$)
DO
  i = INSTR(s$, A$)
  IF i = 0 THEN EXIT DO
  MID$(s$, i, L) = SPACE$(L)
LOOP
END SUB


仍然很慢。

7 楼

一点都不慢!
比我的那破程序快多了。
比如输入10,20
你的程序很快就能出结果,但我的却要等10分钟。

8 楼

DECLARE SUB cc (ss!(), k!)
OPTION BASE 1
CLS
DIM SHARED m, n, f   '全局变量
INPUT m, n
IF m < 1 OR n < 1 OR m > 50 OR n > 50 OR m <> INT(m) OR n <> INT(n) THEN PRINT "ERROR!": END
IF m = n THEN PRINT 1: END
IF m < n THEN x = m ELSE x = n
DIM s(m * n), t(x)
 FOR i = 1 TO x
     t(i) = (x + 1 - i) * (x + 1 - i)     'X为最大的边长, t( ) 储存所有可能的正方形面积
 NEXT i
min = m * n                         '未开始时的最小个数,其实就是最大的可能值,极限值
 p = 0
 i = 0
 lspc = m * n
 v = 1
DO
  i = i + 1
  IF i <= x THEN
     IF lspc >= t(i) THEN
        p = p + 1
        s(p) = t(i)
        lspc = lspc - t(i)
        i = i - 1
        IF lspc = 0 THEN   '如果某些数的平方和等于总面积,尝试拼凑正方形,并得出正方形个数.
           CALL cc(s(), p)
           IF f AND p < min THEN min = p
        END IF
     END IF
  ELSE
     IF p = 1 AND s(p) = 1 THEN v = 0
     i = x + 1 - SQR(s(p))
     lspc = lspc + s(p)
     s(p) = 0
     p = p - 1
  END IF
LOOP UNTIL v = 0
PRINT min
END

SUB cc (ss(), k)
DIM a(m, n)
f = 1
FOR i = 1 TO k
    xxx = m + 1 - SQR(ss(i))
    yyy = n + 1 - SQR(ss(i))
    ff = 0
    FOR ii = 1 TO xxx
        FOR jj = 1 TO yyy
            fff = 0
            IF a(ii, jj) = 0 THEN
               FOR iii = ii TO ii + SQR(ss(i)) - 1
                   FOR jjj = jj TO jj + SQR(ss(i)) - 1
                       IF a(iii, jjj) THEN fff = 0:GOTO nf
                       a(iii, jjj) = 1
               NEXT jjj, iii
               fff = 1
            END IF
            IF fff THEN ff = 1: GOTO sf
nf: NEXT jj, ii
sf:
IF ff = 0 THEN f = 0: EXIT SUB
NEXT i
f = 1
END SUB

Mato其实你用不着过于担心,
就算别人冰雪聪明,
也不一定能看得明白你的代码,
就算给他抄去,也不见得能抢去你的劳动成果.

互相学习才会有进步,
在你这个年龄谈成绩未免太早了.

你的代码具体有没有漏洞或者优化的可能,我没有办法回答你,
因为我也只能是看得出个大概来.
你大概是在穷举所有平方和的可能性,并看它们能不能拼凑成原大正方形,而且个数是否最少的.

但我有一些很哆嗦的建议:
1.因为程序中用不着小数,所以有必要把所有变量弄成整形,在某种程度上可以加快一点运行的效率.
2.虽然说不上反对Goto的使用,但我仍然希望你能避免使用它,因为很多时候你甚至不会知道该怎么修改.

9 楼

我再来说说我的一些想法:

解释一下3楼的代码先:
其实这个程序没经过检查,应该有不少漏洞,我的想法是原来的矩形切去一个正方形后,
将会得到以下一个图形:
         a
        _____
       |    |
  _____|    |
 b|         |d
  |_________|
       c

我的程序里是使用了函数来计算a,b,c,d这个形状的最少正方形的,
但事实上出现了很多的问题,我却没有足够的时间和精力去解决.

而6楼的代码,却是使用字符串来做的.
空格符为没划分的空白,填了字符的为已划分的正方形.
用Instr( )来找到最前面的一个空格,在这个位置上从大到小的测试正方形,
并不断的得到更少的正方形数.
程序是正确的,但当N,M越来越大的时候就显得力不从心了.

10 楼

moz:
我发现你6楼的程序算法是跟我原来的一样的。
Replace1:将第i个正方形的数据放入s$里
Replaces:清除第i个正方形的数据。

不过我不知道KP1函有什么用,而且这个函数没有返回值,怎么搞的?

我来回复

您尚未登录,请登录后再回复。点此登录或注册