银河

SKYIV STUDIO

  博客园 :: 首页 :: 博问 :: 闪存 :: :: :: 订阅 订阅 :: 管理 ::
Timus 1152. The False Mirrors 说述消灭怪物的故事,要求计算出故事中主角受到的最小伤害。

1152. The False Mirrors

Time Limit: 2.0 second
Memory Limit: 16 MB

Background

We wandered in the labyrinth for twenty minutes before finally entering the large hall. The walls were covered by mirrors here as well. Under the ceiling hung small balconies where monsters stood. I had never seen this kind before. They had big bulging eyes, long hands firmly holding riffles and scaly, human-like bodies. The guards fired at me from the balconies, I shot back using my BFG-9000. The shot shattered three mirrors filling the room with silvery smoke. Bullets drummed against my body-armor knocking me down to the floor. Falling down I let go a shot, and got up as fast as I fell down by rotating on my back, like I did in my youth while break dancing, all this while shooting three more times. Three mirrors, three mirrors, three mirrors…
Sergey Lukjanenko, "The Labyrinth of Reflections"

Problem

BFG-9000 destroys three adjacent balconies per one shoot. (N-th balcony is adjacent to the first one). After the shoot the survival monsters inflict damage to Leonid (main hero of the novel) — one unit per monster. Further follows new shoot and so on until all monsters will perish. It is required to define the minimum amount of damage, which can take Leonid.

Input

The first string of input contains integer N, аmount of balconies, on which monsters have taken a circular defense. 3 ≤ N ≤ 20. The second string contains N integers, amount of monsters on each balcony (not less than 1 and no more than 100 on each).

Output

Output file contains one integer, minimum amount of damage.

Sample

inputoutput
7
3 4 2 2 1 4 1
9
Problem Author: Eugene Bryzgalov
Problem Source: Ural Collegiate Programming Contest, April 2001, Perm, English Tour

解答如下:

 1 using System;
 2 using System.IO;
 3 using System.Text.RegularExpressions;
 4 
 5 namespace Skyiv.Ben.Timus
 6 {
 7   // http://acm.timus.ru/problem.aspx?space=1&num=1152
 8   class T1152
 9   {
10     static void Main()
11     {
12       new T1152().Run(Console.In, Console.Out);
13     }
14 
15     void Run(TextReader reader, TextWriter writer)
16     {
17       reader.ReadLine();
18       string[] ss = Regex.Split(reader.ReadLine().Trim(), @"\s+");
19       int[] a = new int[ss.Length]; // 阳台上的怪物数
20       int[] m = new int[ss.Length]; // 阳台被击中的次数
21       int sum = 0, damage = 0, min = int.MaxValue;
22       for (int i = 0; i < a.Length; i++) sum += a[i] = int.Parse(ss[i]);
23       writer.WriteLine(GetDamage(a, m, ref sum, ref damage, min));
24     }
25 
26     int GetDamage(int[] a, int[] m, ref int sum, ref int damage, int min)
27     {
28       if (sum == 0return (min < damage) ? min : damage; // sum: 幸存的怪物个数
29       for (int i = 0; i < a.Length; i++// 遍历幸存的阳台
30       {
31         if (m[i] != 0continue;        // m[i]: 第 i 个阳台被击中的次数
32         damage += sum = Shoot(a, m, sum, i, 11); // 主角开枪后怪物还击
33         if (min > damage) min = GetDamage(a, m, ref sum, ref damage, min);
34         damage -= sum;                   // 回溯,还原主角受到的伤害(damage)
35         sum = Shoot(a, m, sum, i, -10); // 回溯,还原阳台(m)及其上的怪物(sum)
36       }
37       return min; // 消灭全部怪物后主角所受到的最小伤害
38     }
39 
40     int Shoot(int[] a, int[] m, int sum, int i, int sign, int cnt)
41     {
42       for (int j = i; j <= i + 2; j++// 主角每枪击毁三个相邻的阳台
43         if (cnt == (m[j % a.Length] += sign)) sum -= sign * a[j % a.Length];
44       return sum; // 幸存的怪物个数
45     }
46   }
47 }

故事中的主角周围环绕着一圈 N (3 ≤ N ≤ 20) 个阳台,每个阳台上有若干个怪物。主角每开一枪能够摧毁相邻的三个阳台,然后幸存的怪物还击,假设每个怪物每次攻击对主角的伤害都是一个单位。然后开始新一轮枪战,直到消灭所有的怪物。你的任务就是计算出主角受到的最小伤害。

该程序调用 GetDamage() 方法来计算主角所受到的伤害(第 23 行)。该方法首先判断是否还有幸存的阳台(第 28 行),如有的话就遍历幸存的阳台(第 29 行),在循环中调用 Shoot() 方法模拟主角开枪后怪物还击(第 32 行),如果这一轮枪战之后主角所受的伤害(damage)小于消灭全部怪物后主角所受到的最小伤害(min)的话(第 33 行),就递归地调用 GetDamage() 方法自身直到消灭全部怪物(第 33 行)。程序中第 34 和 35 行是 GetDamage() 方法递归调用返回后的回溯,还原主角受到的伤害(damage)和阳台(m)及其上的怪物(sum),以便循环进入下一轮枪战。

下图是在程序的第 27 行之后 ( GetDamage() 方法的入口点 ) 和第 33 行之后 ( GetDamage() 方法递归调用后的返回点 ) 插入适当的调试语句后,经整理后得到的,显示了程序的运行情况。

这个程序的 C++ 语言版本如下:

 1 #include <iostream>
 2 
 3 int n, a[20], m[20];
 4 
 5 int Shoot(int a[], int m[], int sum, int i, int sign, int cnt)
 6 {
 7   for (int j = i; j <= i + 2; j++// 主角每枪击毁三个相邻的阳台
 8     if (cnt == (m[j % n] += sign)) sum -= sign * a[j % n];
 9   return sum; // 幸存的怪物个数
10 }
11 
12 int GetDamage(int a[], int m[], int& sum, int& damage, int min)
13 {
14   if (sum == 0return (min < damage) ? min : damage; // sum: 幸存的怪物个数
15   for (int i = 0; i < n; i++// 遍历幸存的阳台
16   {
17     if (m[i] != 0continue;        // m[i]: 第 i 个阳台被击中的次数
18     damage += sum = Shoot(a, m, sum, i, 11); // 主角开枪后怪物还击
19     if (min > damage) min = GetDamage(a, m, sum, damage, min);
20     damage -= sum;                    // 回溯,还原主角受到的伤害(damage)
21     sum = Shoot(a, m, sum, i, -10); // 回溯,还原阳台(m)及其上的怪物(sum)
22   }
23   return min; // 消灭全部怪物后主角所受到的最小伤害
24 }
25 
26 int main()
27 {
28   std::cin >> n;
29   int sum = 0, damage = 0, min = INT_MAX;
30   for (int i = 0; i < n; i++)
31   {
32     std::cin >> a[i];
33     sum += a[i];
34   }
35   std::cout << GetDamage(a, m, sum, damage, min) << std::endl;
36 }

本程序的运行时间是: C#: 0.39 秒,C++: 0.343 秒:

但这道题最好的运行时间是 0.001 秒:

有谁知道更好的算法吗?

posted on 2008-06-28 14:00  银河  阅读(2547)  评论(3编辑  收藏  举报