LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1141|回复: 8

删数问题(无输出)

[复制链接]
发表于 2008-11-16 15:30:39 | 显示全部楼层 |阅读模式
我知道这个东西发到这里可能不太合适,但这个问题我问了好多地方,实在是没有结果,希望各位大牛能帮忙解释下。谢谢。
【问题描述】输入一个高精度的大正整数S(S最长可达240位),去掉其中任意N位数字后剩下的数字按原次序组成一个新的正整数S’。编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数S’最小。
【输入形式】输入有两行:
1.第一行是大整数S。其中S最长可达240位。
2.第二行是整数N。S、N均以非0数字开头。
【输出形式】输出有一行,是在S中删除N位后所得的最小数字S’。
【样例输入1】
178543
4
【样例输出1】13

【样例输入2】
1002
1
【样例输出2】002

【样例说明】样例1中输入整数S=178543,N=4,要求在178543中删除4位,使剩下的数字最小。正确答案为S’ = 13。样例2中输入整数S=1002,N=1,删完一位后S’ = 002,而不是2,即2之前的0也必须输出。
【运行时限】程序一次运行的最长时间限制在15秒内,超出则认为程序错误。

下面是我写的程序:

  1. #include <stdio.h>
  2. #include <string.h>

  3. int main()
  4. {
  5.         char integer[250], result[250];
  6.         int start = 0, length, strlength, n, i = 0, j, k = 0;

  7.         fgets(integer, 249, stdin);
  8.         strlength = strlen(integer);
  9.         integer[strlength - 1] = '\0';
  10.         scanf("%d", &n);
  11.         length = strlength - n;
  12.         for (i = 0; i <= 9; i++)
  13.                 for (j = start; j < length && length <= strlength; j++)
  14.                         if (integer[j] == i) {
  15.                                 result[k++] = i;
  16.                                 length++;
  17.                                 start = j + 1;
  18.                         }
  19.         fputs(result, stdout);
  20.         return 0;
  21. }
复制代码


思路是:
从头开始由0-9开始检测,到末尾前所需位数为止,遇到最小的数字就放到result里,如果有,继续检测,没有,责从头再来。最后打出result。
现在的问题是,编译没错误,但没有输出。
搞了很长时间,没弄明白。
请高手帮解决一下,再次感谢。 
发表于 2008-11-16 16:51:44 | 显示全部楼层
姑且先不论你的算法正确与否,根据你的程序:for (i = 0; i <= 9; i++); result[k++] = i; 最后输出的result数组的每个单元都是0到9,以字符方式显示的话,ascii码0结束符,1-9都是控制符,当然不显示。
例如result[0]==1 ,你要显示的话就要先result[0] += '0'; 就能显示了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-11-16 17:54:35 | 显示全部楼层
把fputs改成
  1.         for (i = 0; i < k; i++)
  2.                 printf("%d", result[i] + '0');
复制代码

还是不行。
回复 支持 反对

使用道具 举报

发表于 2008-11-16 19:29:00 | 显示全部楼层
从表达上来讲, 楼主可能愿意把
for (i = 0; i <= 9; i++)
改为
for (i = '0'; i <= '9'; i++)

P.S. 最近在看 Haskell, 用 Haskell 写了一个, 提供四种解法, solve 看起来是穷举, 但是根据 haskell 的惰性求值原则, 实际上与 solve2 应该是一样的. solve2 则是手工根据各种不同的情况来进行处理. solve3 换了一种递归策略, solve4 进一步对 solve3 进行了归纳, 于是算法本身也描述得很清晰了. 由于本来也没打算让大家看明白, 因此注释就不加了, 纯当娱乐
  1. main = do
  2.     let (s, n) = ("1219", "1")
  3.     --let (s, n) = ("172839", "3")
  4.     --let (s, n) = ("312", "4")
  5.     --let (s, n) = ("312", "1")
  6.     --let (s, n) = ("555678", "3")
  7.     --let (s, n) = ("555432", "3")
  8.     --let (s, n) = ("555632", "3")
  9.     --let (s, n) = ("555478", "3")
  10.     --(s, n) <- input
  11.     let result = solve s (read n)
  12.     putStrLn ("result: " ++ result)
  13.     let result2 = solve2 s (read n)
  14.     putStrLn ("result2: " ++ result2)
  15.     let result3 = solve3 s (read n)
  16.     putStrLn ("result3: " ++ result3)
  17.     let result4 = solve4 s (read n)
  18.     putStrLn ("result4: " ++ result4)
  19.     return ()
  20. input :: IO (String, String)
  21. input = do
  22.     putStrLn "input S:"
  23.     s <- getLine
  24.     putStrLn "input N:"
  25.     n <- getLine
  26.     return (s, n)
  27. solve :: String -> Int -> String
  28. solve s 0 = s
  29. solve s n
  30.     | (n >= length s) = ""
  31. solve (s0 : ss) n =
  32.     min (s0 : solve ss n) (solve ss (n - 1))
  33. solve2 :: String -> Int -> String
  34. solve2 s 0 = s
  35. solve2 s n
  36.     | (n >= length s) = ""
  37. solve2 (s0 : s1 : []) 1
  38.     | (s0 <  s1) = s0 : []
  39.     | (s0 >= s1) = s1 : []
  40. solve2 (s0 : s1 : ss) 1
  41.     | (s0 <= s1) = s0 : solve2 (s1 : ss) 1
  42.     | (s0 >  s1) = s1 : ss
  43. solve2 (s0 : s1 : ss) n
  44.     | (s0 <  s1) = s0 : solve2 (s1 : ss) n
  45.     | (s0 >  s1) = solve2 (s1 : ss) (n - 1)
  46.     | (s0 == s1) = solve2 (s0 : solve2 (s1 : ss) (n - 1)) 1
  47. solve3 :: String -> Int -> String
  48. solve3 s 0 = s
  49. solve3 (s0 : s1 : []) 1
  50.     | (s0 <  s1) = s0 : []
  51.     | (s0 >= s1) = s1 : []
  52. solve3 (s0 : s1 : ss) 1
  53.     | (s0 <= s1) = s0 : solve3 (s1 : ss) 1
  54.     | (s0 >  s1) = s1 : ss
  55. solve3 s n =
  56.     solve3 (solve3 s (n - 1)) 1
  57.    
  58. solve4 :: String -> Int -> String
  59. solve4 s 0 = s
  60. solve4 (s0 : []) 1 = []
  61. solve4 (s0 : s1 : ss) 1
  62.     | (s0 <= s1) = s0 : solve4 (s1 : ss) 1
  63.     | (s0 >  s1) = s1 : ss
  64. solve4 s n = solve4 (solve4 s (n - 1)) 1
  65.    
  66. -- vim: set ts=4 sts=4 sw=4 et ai :
复制代码
回复 支持 反对

使用道具 举报

发表于 2008-11-16 22:00:44 | 显示全部楼层
S到达240位的时候穷举不现实,需要线性规划单纯形法来解。

回xliotx:你用了%d就不需要+'0'了。+'0'的话用%c
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-11-17 20:12:33 | 显示全部楼层
由于上课,不经常上网,所以回复的比较晚,谢谢楼上两位。
昨天看一楼的回复,晚上躺床上想到的,是数字和字符关系弄混,原来的算法也有些问题.
以下是我改过的.通过测试。

  1. #include <stdio.h>
  2. #include <string.h>

  3. /* scan from 0 - 9, test the input, rescan if get the result. */
  4. int main()
  5. {
  6.         char integer[250], result[250];
  7.         int start = 0, strlength, n, i = 0, j, temp, k = 0;

  8.         fgets(integer, 249, stdin);
  9.         strlength = strlen(integer) - 1;
  10.         integer[strlength] = '\0';
  11.         scanf("%d", &n);
  12.         for (i = 0; i <= 9; i++) {
  13.                 temp = i;
  14.                 for (j = start; j <= n && n < strlength; j++)
  15.                         if (integer[j] == temp + '0') {
  16.                                 result[k++] = temp + '0';
  17.                                 n++;
  18.                                 start = j + 1;
  19.                                 i = -1;
  20.                                 break;
  21.                         }
  22.         }
  23.         for (i = 0; i < k; i++)
  24.                 printf("%c", result[i]);
  25.         return 0;
  26. }
复制代码

思路是要在从左到右扫描一次,在满足所剩长度的前提下,取最小数作第一位(0-9的扫描),扫到后,从这位开始,重新开始扫描最小数。重复做下去,直到去满所要求的位数。

再次感谢大家的帮助。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-11-17 20:15:15 | 显示全部楼层
搜索了一下Haskell 语言,挺有意思的。
但目前3楼的高人写的东西敝人基本看不懂...
回复 支持 反对

使用道具 举报

发表于 2008-11-17 21:23:08 | 显示全部楼层
我也只是刚开始学, 写着玩的. 由于 Haskell 的表达方式还没有习惯, 所以除了第一种穷举外, 后面几种代码上的演进也表明了我向 Haskell 的思维方式的逐渐演变. 解释一下最后一种算法, 就当做广告吧
  1. -- 这是注释行
  2. -- 这一行可以看作是函数的类型声明
  3. solve4 :: String -> Int -> String
  4. -- 下面开始求解
  5. -- 从一个序列中删除 0 个数, 显然不做任何改动
  6. solve4 s 0 = s
  7. -- 从一个仅包含一个数的序列中删除一个数, 得到的是空序列
  8. solve4 (s0 : []) 1 = []
  9. -- 从一个长度大于 1 的序列中删除 1 个数, 要再分两种情况进行讨论
  10. --  -- 当第一个数字大于第二个数字时, 显然删去第一个数字即得到结果
  11. --  -- 当第一个数字不大于第二个数字时, 保留第一个数, 并在剩下的子序列中进行删一个数的操作  
  12. solve4 (s0 : s1 : ss) 1
  13.     | (s0 <= s1) = s0 : solve4 (s1 : ss) 1
  14.     | (s0 >  s1) = s1 : ss
  15. -- 删除 n 个数, 就是进行 n 次 "删除一个数" 的操作
  16. solve4 s n = solve4 (solve4 s (n - 1)) 1
复制代码

Haskell 的表达与数学归纳法非常像, 通过逐渐减小问题的规模, 直至边界情况, 来简化并最终解决问题. 把上面的 4 种情况概括一下, 就是

重复 N 次:
    寻找从序列左边第一个元素开始的最长单调非降子序列, 删除其最后一个元素.

所谓单调非降子序列, 就是满足 S_i <= S_{i+1} 的子序列.
回复 支持 反对

使用道具 举报

发表于 2008-11-18 12:48:13 | 显示全部楼层
solve4 的算法,是 O(N^2) 级别,但是对此算法进行分析我们可发现,N 次删除一个数的操作中,每一次都有决大部分的遍历是重复的,对此我们可再进行一些优化。在分析算法之前,先帖一段 Haskell 代码

  1. solve5 s n =
  2.     reverse $ helper [] s n
  3.     where
  4.         helper :: String -> String -> Int -> String
  5.         helper d         s@([]   ) 0 = d
  6.         helper d         s@(s':ss) 0 = helper (s':d) ss 0
  7.         helper d@([]   ) s@(s':ss) n = helper (s':[]) ss n
  8.         helper d@(d':ds) s@([]   ) n = helper ds [] (n - 1)
  9.         helper d@(d':ds) s@(s':ss) n
  10.             | (d' >  s')             = helper ds s (n - 1)
  11.             | (d' <= s')             = helper (s':d) ss n
复制代码


用自然语言进行描述的话,就是:
(1) 将原始序列视为一个栈 s,右则数据在栈底,左侧数据在栈顶
(2) 另准备一个栈 d 来保存结果
(3) 待删除数的个数为 n
(4) 当 d 为空时,将 s 弹栈并压入 d
(5) 当 d 有数据时:
(5a)   比较 d 与 s 的栈顶 top(d) 与 top(s),如果 top(d) > top(s),d 弹栈,同时 n <- (n - 1)
(5b)   如果 top(d) <= top(s),s 弹栈并压入 d 中
(6) 如果 s 栈空,则 d 弹栈,同时 n <- (n - 1)
(7) 如果 n != 0,则从 (4) 重新开始
(8) 如果 n 已为零时,将 s 依次弹栈并压入 d 中
(9) d 中保存的是最终结果,左侧数据在栈底,右侧数据在栈顶

此算法中,对 s 遍历一次,同时 d 要进行 n 次扔数据的操作。如果 s 的长度为 m,则最终时间复杂度为 O(m + n)

用 C 来实现的话

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <time.h>

  5. void randStr(char * s, int m)
  6. {
  7.         while (m--) {
  8.                 *(s++) = rand() / (RAND_MAX + 1.0) * 10.0 + '0';
  9.         }
  10.         s[m] = 0;
  11. }

  12. void solve(char * d, char * s, int n)
  13. {
  14.         char * d0 = d--;

  15.         for ( ; n; --d, --n) {
  16.                 while (d < d0 || *d <= *s) {
  17.                         *(++d) = *(s++);
  18.                 }
  19.         }
  20.         while (*(++d) = *(s++)) ;
  21. }
  22. int main(void)
  23. {
  24.         srand(time(NULL));
  25.         int m = 100000000;
  26.         int n = m - 100;

  27.         char * s = malloc(m + 1);
  28.         randStr(s, m);

  29.         char * d = malloc(m);
  30.         printf("begin\n");
  31.         solve(d, s, n);
  32.         printf("d: %s\n", d);

  33.         free(d);
  34.         free(s);
  35.         return 0;
  36. }
复制代码

其中 randStr() 是随机数列生成器,solve() 是解答器

完整的 Haskell 则如下

  1. import IO
  2. import Random

  3. main = do
  4.     s <- randStr 1000000
  5.     let n = "999900"
  6.     print "begin"
  7.     let result5 = solve5 s (read n)
  8.     putStrLn ("result5: " ++ result5)
  9.     return ()

  10. randStr :: Int -> IO String
  11. randStr 0 = return []
  12. randStr n = do
  13.     num <- randomRIO('0', '9')
  14.     str <- randStr (n - 1)
  15.     return (num : str)

  16. solve5 :: String -> Int ->String
  17. solve5 s n =
  18.     reverse $ helper [] s n
  19.     where
  20.         helper :: String -> String -> Int -> String
  21.         helper d         s@([]   ) 0 = d
  22.         helper d         s@(s':ss) 0 = helper (s':d) ss 0
  23.         helper d@([]   ) s@(s':ss) n = helper (s':[]) ss n
  24.         helper d@(d':ds) s@([]   ) n = helper ds [] (n - 1)
  25.         helper d@(d':ds) s@(s':ss) n
  26.             | (d' >  s')             = helper ds s (n - 1)
  27.             | (d' <= s')             = helper (s':d) ss n

  28. -- vim: set ts=4 sts=4 sw=4 et ai :
复制代码

同样,randStr 是随机数列生成器,solve5 是解答器

下面来看一看运行效率

  1. $ gcc -O3 main.c && time ./a.out
  2. begin
  3. d: 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

  4. real        0m4.600s
  5. user        0m4.532s
  6. sys        0m0.064s

  7. $ ghc -O3 Main.hs && time ./a.out
  8. "begin"
  9. result5: 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

  10. real        0m3.892s
  11. user        0m3.748s
  12. sys        0m0.144s
复制代码


我们可以看到,Haskell 中是 1000000 个数据保留 100 个,C 中是 100000000 个数据保留 100 个,二者的耗时在同一量级,因此 Haskell 的速度大约比 C 慢 100 倍。不过值得说明的是,两个程序的耗时中,实际上大半都是花在随机数生成过程中,因此具体计算部分的耗时有待更严密的比较
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表