银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::

Timus 1727. Znaika's Magic Numbers 要求将指定的正整数分解为一些不同的正整数的和。


1727. Znaika's Magic Numbers

Time Limit: 0.5 second
Memory Limit: 64 MB
Znaika has many interests. For example, now he is investigating the properties of number sets. Znaika writes down some set consisting of different positive integers (he calls this set agenerating set), calculates the sum of all the written digits, and writes down the result in a special notebook. For example, for a generating set 7, 12, 43, he will write down the number17 = 7 + 1 + 2 + 4 + 3. Znaika is sure that only magic numbers can appear as a result of this operation.
Neznaika laughs at Znaika. He thinks that there is a generating set for every number, and he even made a bet with Znaika that he would be able to construct such a set.
Help Neznaika win the bet and construct a generating set for a given number.

Input

The only input line contains an integer n (0 < n < 105).

Output

If it is possible to construct a generating set for the number n, output the number of elements in this set in the first line. In the second line output a space-separated list of these elements. The elements of the set must be different positive integers strictly less than 105. If there are several generating sets, output any of them. If there are no generating sets, output −1.

Sample

inputoutput
17
3
7 12 43
Problem Author: Ivan Burmistrov
Problem Source: Ural Regional School Programming Contest 2009

Tags: none


题意

这道题目要求将一个指定的正整数(小于105)分解为一些不同的正整数(也必须小于105)之和,这些正整数的各位数字和加起来刚好等于指定的正整数。

解答

使用 C# 语言解答如下:

using System;

// http://acm.timus.ru/problem.aspx?space=1&num=1727
static class Timus
{
  static void Main()
  {
    int n = int.Parse(Console.ReadLine());
    int count = Compute(ref n), tops = count;
    for (int i = 0; n > 9; i++, n -= 10) count++;
    int tens = count - tops;
    Console.WriteLine(count += ((n > 0) ? 1 : 0));
    for (int i = 99999; tops-- > 0; i--) Console.Write(i + " ");
    while (tens-- > 0) Console.Write("19 28 37 46 ".Substring(tens * 3, 3));
    if (n > 0) Console.Write(n);
  }
  
  static int Compute(ref int n)
  {
    int count = 0;
    for (int sum = 45; n >= 45; )
    {
      n -= sum--;
      if (++count % 10 == 0) sum += 9;
      if (count % 100 == 0) sum += 9;
      if (count % 1000 == 0) sum += 9; 
    }
    return count;
  }
}

分析如下:

  • 首先从 99999 开始往下倒数,直到 n 小于 45。注意,99999 的各位数字之和刚好等于 45,这也是最大的各位数字之和了。
  • 然后利用 19、28、37、46 这四个各位数字之和刚好为 10 的数字。
  • 最后 n 必然小于 10,如果 n 不为零的话,直接输出 n。
  • Compute 方法就是执行从 99999 开始往下倒数的过程。其各位数字和是很有规律的,也就是从 45 开始往下递减,每遇到整十、整百、整千再加 9。

此外,还可以使用随机算法,如下所示:

using System;
using System.Linq;
using System.Collections.Generic;

// http://acm.timus.ru/problem.aspx?space=1&num=1727
static class Timus
{
  static void Main()
  {
    var n = int.Parse(Console.ReadLine());
    var set = Compute(ref n);
    var count = set.Count;
    for (var i = 0; n > 9; i++, n -= 10) count++;
    var tens = count - set.Count;
    Console.WriteLine(count += ((n > 0) ? 1 : 0));
    foreach (var i in set) Console.Write(i + " ");
    while (tens-- > 0) Console.Write("19 28 37 46 ".Substring(tens * 3, 3));
    if (n > 0) Console.Write(n);
    Console.WriteLine();
  }
  
  static HashSet<int> Compute(ref int n)
  {
    var set = new HashSet<int>();
    for (var rand = new Random(); n >= 45; )
    {
      var k = rand.Next(100, 100000);
      if (set.Add(k)) n -= k.ToString().Select(x => x - '0').Sum();
    }
    return set;
  }
}

这个程序使用 HashSet 保存随机生成的正整数,以便丢弃重复的正整数,以及稍后输出这些正整数。注意第 28 行使用 Select 及 Sum 扩展方法进行各位数字求和。

第一个程序运行时间是 0.14 秒,第二个程序也不慢,运行时间是 0.156 秒。

让我们进行一下测试吧:

using System;
using System.Linq;
using System.Diagnostics;

static class Tester
{
  static void Main(string[] args)
  {
    var n = (args.Length == 0) ? 100000000 : int.Parse(args[0]);
    Run(n, DigitsSum1);
    Run(n, DigitsSum2);
  }
  
  static void Run(int n, Func<int, int> func)
  {
    var timer = Stopwatch.StartNew();
    long sum = 0;
    for (var i = 0; i <= n; i++) sum += func(i);
    timer.Stop();
    Console.WriteLine("{0} {1:N0} {2:N0}", timer.Elapsed, n, sum);
  }
  
  static int DigitsSum1(this int n)
  {
    var sum = 0;
    for (; n > 0; n /= 10) sum += n % 10;
    return sum;
  }
  
  static int DigitsSum2(this int n)
  {
    return n.ToString().Select(x => x - '0').Sum();
  }
}

这个程序的运行结果如下所示:

E:\work>tester
00:00:20.3631972 100,000,000 3,600,000,001
00:00:58.6052102 100,000,000 3,600,000,001

看来使用 Linq 进行求和也不会很慢。


返回目录

posted on 2011-11-23 20:49  银河  阅读(718)  评论(6编辑  收藏  举报