在使用 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。