回 帖 发 新 帖 刷新版面

主题:优化算法!紧急!

输入N个数(5<=N<=100),要求把它们分成M组,分组时要符合三个要求:
(1)每组至少有一个数。
(2)每组除第一个数外,每一个数都不能小于前一个数。
(3)每组的所有数必须都是N个数中的,且不能改变位置(即原来N个数中,I在J之前,则I、J如果分在同一组的话,I还要在J之前)。
求怎么分才能使M最小,输出M及分法。
【样例输入】
N= 6


11 


13 

【样例输出】
M= 2
4 5 6 8 13
11

回复列表 (共8个回复)

沙发

除了枚举、递归、回溯之外的所有算法都给分。

板凳

我已经写出来了程序:
DECLARE SUB nodown (s!())
DECLARE SUB clearzero (s!())
DECLARE FUNCTION nozero! (sss!())
CLS
INPUT nn
DIM SHARED ss(1 TO nn, 1 TO 2)
DIM a(1 TO nn)
FOR i = 1 TO nn: INPUT a(i): NEXT i
DO
  CALL nodown(a())
  FOR i = UBOUND(ss, 1) TO 1 STEP -1
      PRINT ss(i, 1);
      a(ss(i, 2)) = 0
  NEXT i
  PRINT
  CALL clearzero(a())
  m = m + 1
LOOP UNTIL nozero(a()) = 0
PRINT "M="; m
END

SUB clearzero (s())
p = nozero(s())
IF p = 0 THEN EXIT SUB
DIM s2(1 TO p)
pp = 0
FOR i = 1 TO UBOUND(s)
    IF s(i) <> 0 THEN
       pp = pp + 1: s2(pp) = s(i)
    END IF
NEXT i
REDIM s(1 TO pp)
FOR i = 1 TO pp: s(i) = s2(i): NEXT i
END SUB

SUB nodown (s())
n = UBOUND(s)
DIM t(1 TO n), m(1 TO n)
t(1) = 1: m(1) = 0
FOR i = 2 TO n
     k = 0: x = 0
     FOR j = i - 1 TO 1 STEP -1
         IF s(i) >= s(j) AND k < t(j) THEN
            k = t(j)
            x = j
         END IF
     NEXT j
     t(i) = k + 1
     IF k <> 0 THEN m(i) = x
NEXT i
k2 = 1: kk = 1
FOR i = 2 TO n
    IF t(i) > k2 THEN k2 = t(i): kk = i
NEXT i
REDIM ss(1 TO k2, 1 TO 2)
FOR i = 1 TO k2
    ss(i, 1) = s(kk)
    ss(i, 2) = kk
    kk = m(kk)
NEXT i
END SUB

FUNCTION nozero (sss())
nz = 0
FOR i = 1 TO UBOUND(sss)
    IF sss(i) <> 0 THEN nz = nz + 1
NEXT i
nozero = nz
END FUNCTION

REM 子程序说明:
REM 过程nodown:对数组S中的元素求最长的非降序列的过程,序列存放到ss数组中。
REM 过程clearzero:将数组S中的所有零清除掉,只保留非零数字。
REM 函数nozero:求数组SSS中非零元素的个数的函数。

现在程序是写好了,可是问题又出来了:
(1)这个程序是采用一种贪心的思路编的,即每次都取最长的非降序列。这样对某些数据就通不过了,如
N=7
3
8
4
9
5
10
6
最少只要分2组(3,4,5,6)和(8,9,10),但我的程序却把它分成了3组。
(2)这个程序的M是在最后输出的,而样例输出中的却是最先输出的,怎么办?

3 楼

算法名词我不懂。

但我有个想法:
对n个元素,就建立n*n的二维数组来存放结果。(显然有点奢侈了)
把第一个元素放在结果数组的最开始;
对第二个元素开始以下的每一个元素,检查结果数组的每一维的最后一个非零元素,如果大于当前元素,检查下一维;否则写入其后;所有非空维都检查完了,仍不能写入,则写入下一维的开头。
处理完后,输出非空维数,输出各维元素。

4 楼

N=7
3,8,4,9,5,10,6
3,8,  9,  10
    4,  5,   6

5 楼

用“取大优先”(这里是取小优先)的方法一般可以找到较优解:

寻找第一个被保留的数(存放在BaoLiu()数组中),它是未选数列中最小的数;下一个数就是当前数;

当前数与最后一个BaoLiu()数比较,如果小,则剔除最后一个BaoLiu()数,然后继续比较;大则保留,作为最后一个BaoLiu()数(不应该有相等的情况吧?如果相等也剔除),下一个数就是当前数,重复这一过程,这样就能得到一个由最小数开头的数列,输出它们,并清空BaoLiu()数组;

然后再在被剔除的数中重复上面的过程,直到被剔除的数的个数小于2(1个或0个)。

注意这是个较优解,如果不是很特别,它一般就是最优解了。

6 楼

上面只是一个大体的算法,写代码时还要注意一些细节。

7 楼

更简化的方案:

A、所有的数均为未选数;
B、在未选数中找到最小数,输出该数,并在数列中剔除之;
C、之后的数列均为未选数,如果个数大于1,则->B;等于1,则输出之,并在数列中剔除之;并标记完成一组(包括个数为0);
D、所有未剔除的数的个数大于1,则->A;等于1,则输出之,并标记完成工作(包括个数为0)。

这个算法差不多可以直接写代码了。

里面有两个过程,一个是找最小数,一个是在数列中剔除一个数。

8 楼

程序出来了:

'下面的程序不保证M最小,只能说在一般情况下M应该是最小的
'
DEFINT A-Z
RANDOMIZE TIMER
INPUT "n=0:exit, n=[5,99] ", n
IF n = 0 THEN END
IF n > 99 THEN n = 99
IF n < 5 THEN n = 5
PRINT "n="; n
DIM a(n) AS INTEGER
FOR i = 1 TO n
a(i) = RND * 100
PRINT a(i);
Next
'以上生成并显示n个随机整数
PRINT
Print
m = 1
Print m;"    ";
b = a(1)
w = 1
'上二行为初始定义,b是最小值,w是最小值的位置,以下进入主题
DO WHILE n > 1
FOR i = w + 1 TO n
  IF a(i) < b THEN b = a(i): w = i
NEXT
PRINT b;
'以上是找出最小数并显示之
IF w = n THEN
  Print
  m=m+1
  Print m;"    ";
  b = a(1)
  w = 1
  '以上如果最小数是最后一个数,则结束一轮
ELSE
  FOR i = w + 1 TO n
    a(i - 1) = a(i)
  Next
  '如果最小数不是最后一个数,则后面的数列均前移一位(刚才找到的最小数被覆盖)
END IF
n = n - 1
b = a(w)
'上二行定义新的查找范围(注意这里有语句共用,本人的不良习惯,结构化的编程不要这样写)
LOOP
IF n = 1 THEN PRINT a(n)
PRINT "game over."
END

运行的结果还比较满意:

70 69 70 19 79 70 6 15 11

1  6 11
2  15  
3  19 70
4  69 70 79
5  70


19 6 4 76 32 45 78 6 8 39 55 25 28 77 74

1  4 6 8 25 28 74
2  6 32 39 55 77
3  19 45 78
4  76

我来回复

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