在使用 C# 语言解 ACM 题的时候,如果能够有一个 ReadInt32 方法直接从标准输入读取整数比较方便的。下面就是一个 I/O 助手类 IOHelper:
namespace Skyiv { using System; using System.IO; using System.Text; sealed class IOHelper : IDisposable { static readonly Encoding Encoding = Encoding.ASCII; // or UTF8 ? static readonly byte[] EOLS = Encoding.GetBytes(Environment.NewLine); static readonly byte[] BLANKS = { 9, 10, 13, 32 }; // tab,lf,cr,space static readonly byte EOF = 0; // assume '\0' not in input file byte[] buf = new byte[32]; // for Write(int n) byte[] buffer = new byte[64 * 1024]; int current = 0; int count = 0; BinaryReader reader; BinaryWriter writer; public IOHelper() : this(Console.OpenStandardInput(), Console.OpenStandardOutput()) {} public IOHelper(Stream reader, Stream writer) { this.reader = new BinaryReader(reader); this.writer = new BinaryWriter(writer); } byte ReadByte() { if (current >= count) { count = reader.Read(buffer, current = 0, buffer.Length); if (count == 0) return EOF; } return buffer[current++]; } public static byte[] GetBytes(string str) { return Encoding.GetBytes(str); } public int ReadInt32() { var n = 0; var ok = false; for (byte b; (b = ReadByte()) != EOF; ) { if (Array.IndexOf(BLANKS, b) >= 0) if (ok) break; else continue; n = n * 10 + (b - '0'); ok = true; } return n; } public int ReadLine(byte[] buffer) { var n = 0; while (n < buffer.Length) { var b = ReadByte(); if (b == EOLS[0]) { if (EOLS.Length == 2 && ReadByte() != EOLS[1]) throw new InvalidDataException("Invalid EOL"); break; } buffer[n++] = b; } return n; } public void Write(int n) { if (n == 0) { writer.Write((byte)'0'); return; } var i = buf.Length; for (; n > 0; n /= 10) buf[--i] = (byte)((n % 10) + '0'); Write(buf, i, buf.Length - i); } public void Write(byte[] buffer, int index, int count) { writer.Write(buffer, index, count); } public void Write(string str) { var buffer = Encoding.GetBytes(str); writer.Write(buffer, 0, buffer.Length); } public void WriteSpace() { writer.Write(BLANKS, 3, 1); } public void WriteLine() { writer.Write(EOLS, 0, EOLS.Length); } public void Dispose() { if (reader != null) reader.Close(); if (writer != null) writer.Close(); } } }
此外,IOHelper 类还提供以下方法用于处理字符串:
- public int ReadLine(byte[] buffer)
- public void Write(byte[] buffer, int index, int count)
类似于 C/C++ 语言,这两个方法仅仅把字符串当作字节数组处理,使用 ASCII 码。而不是象 C# 语言中字符串是使用 Unicode 进行编码。
下面是一个使用示例,题目来源请参见“I-Keyboard”这篇随笔。
namespace Skyiv.Ben.Acm { using System; // http://www.spoj.pl/problems/IKEYB/ sealed class Ikeyb { const int MAX = 90; static int[,] cost = new int[MAX + 1, MAX + 1]; static int[,] price = new int[MAX + 1, MAX + 1]; static int[,] index = new int[MAX + 1, MAX + 1]; static int[] F = new int[MAX]; static byte[] keys = new byte[MAX]; static byte[] letters = new byte[MAX]; static byte[] message1 = IOHelper.GetBytes("Keypad #"); static byte[] message2 = IOHelper.GetBytes(": "); static IOHelper helper; static void Main() { using (helper = new IOHelper()) { var runner = new Ikeyb(); var T = helper.ReadInt32(); for (var n = 1; n <= T; n++) runner.Run(n); } } void Run(int n) { var K = helper.ReadInt32(); var L = helper.ReadInt32(); helper.ReadLine(keys); helper.ReadLine(letters); for (var i = 0; i < L; i++) F[i] = helper.ReadInt32(); Initialize(K, L); Compute(K, L); helper.Write(message1, 0, message1.Length); helper.Write(n); helper.Write(message2, 0, 1); helper.WriteLine(); Output(K, L); helper.WriteLine(); } void Initialize(int K, int L) { for (var i = 0; i <= K; i++) for (var j = 1; j <= L; j++) price[i, j] = int.MaxValue / 2; for (var i = 1; i <= L; i++) for (var j = i; j <= L; j++) cost[i, j] = cost[i, j - 1] + (j - i + 1) * F[j - 1]; } void Compute(int K, int L) { for (var i = 1; i <= K; i++) for (var j = i; j <= L; j++) for (var n = 1; n <= j - i + 1; n++) { var sum = price[i - 1, j - n] + cost[j - n + 1, j]; if (sum <= price[i, j]) { price[i, j] = sum; index[i, j] = n; } } } void Output(int K, int L) { if (K == 0) return; var n = index[K--, L]; Output(K, L - n); helper.Write(keys, K, 1); helper.Write(message2, 0, message2.Length); helper.Write(letters, L - n, n); helper.WriteLine(); } } }
如果给出以下输入:
1 2 5 *# ABCDE 1024 32768 2147483647 987 654321
上述程序将产生以下输出:
Keypad #1: *: ABCD #: E
注意上述输出其实是有问题的,正确的输出应该分为 AB 和 CDE 两组。但是程序本身是没有问题的,而是输入数据有问题。因为原来的题目中限定各个字母出现的频率不能超过 100000。