using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
public static void Main()
{
string textmap = @"煩悩煩悩煩煩悩煩
煩煩悩空悩悩悩悩
悩空悩空悩空悩煩
空煩煩悩悩悩煩煩
悩悩悩煩悩煩煩悩
煩悩空空悩煩悩煩
煩煩煩悩煩空悩煩";
Console.WriteLine("鐘の数は{0}個です.", Func160(textmap));
}
public static int Func160(string textmap)
{
//8方向
var dirs = Enumerable.Range(0, 9).Where(x => x != 4).Select(x => new { X = x % 3 - 1, Y = x / 3 - 1 });
//テキストマップをint配列に変換
var lines = textmap.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries).ToArray();
var width = lines.Max(x => x.Length);
var height = lines.Length;
var N = width * height;
var tmp = new int[height, width];
for (var i = 0; i < height; i++)
{
var line = lines[i];
for (var j = 0; j < width; j++)
{
var letter = line.Substring(j, 1);
tmp[i, j] = letter == "煩" ? 1 : letter == "悩" ? 2 : 0;
}
}
//int配列を隣接リスト形式の有向グラフに変換
//煩⇒悩をエッジとし、ソースとシンクを追加しておく
//ソースから辺が伸びているノードのリスト
var src = new bool[N];
//ノード間の隣接リスト
var map = new int[N, 8];
//シンクへ辺が伸びているノードのリスト
var snk = new bool[N];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
var nIndex = 0;
var isAdded = false;
foreach (var dir in dirs)
{
var x = j + dir.X;
var y = i + dir.Y;
if (tmp[i, j] == 1 && x >= 0 && y >= 0 && x < width && y < height && tmp[y, x] == 2)
{
if (!isAdded)
{
isAdded = true;
src[i * width + j] = true;
}
map[i * width + j, nIndex++] = y * width + x;
snk[y * width + x] = true;
}
}
for (; nIndex < 8; nIndex++)
map[i * width + j, nIndex] = -1;
}
}
//対象グラフは2部グラフなので最小カバー=最大フロー
//なので最大フローを求める
var result = 0;
var isChanged = false;
do
{
isChanged = false;
var isVisited = new bool[N];
//ソースに隣接した各ノードから深さ優先探索
for (var i = 0; i < N; i++)
{
if (!src[i] || isVisited[i])
continue;
isVisited[i] = true;
var stack = new Stack<int>();
stack.Push(i);
while (stack.Count > 0)
{
var node = stack.Peek();
//経路を発見
if (snk[node])
{
isChanged = true;
//フローを1増やす
result++;
src[i] = false;
snk[node] = false;
stack.Pop();
//経路を逆順で処理
while (stack.Count > 0)
{
var prev = stack.Pop();
var added = false;
for (int j = 0; j < 8; j++)
{
//順方向のエッジを削除
if (map[prev, j] == node)
{
for (int k = j; k < 7; k++)
map[prev, k] = map[prev, k + 1];
map[prev, 7] = -1;
}
//残余グラフに逆方向のエッジを追加
if (!added && map[node, j] == -1)
{
added = true;
map[node, j] = prev;
}
}
node = prev;
}
//到達情報が変更されたのでリセット
Array.Clear(isVisited, 0, N);
}
//まだ経路が見つかっていない
else
{
var next = -1;
for (int j = 0; j < 8; j++){
var t = map[node, j];
if (t != -1 && !isVisited[t])
{
next = t;
break;
}
}
//行き止まりなら1つ戻る
if (next == -1)
stack.Pop();
else
{
//次があれば進む
isVisited[next] = true;
stack.Push(next);
}
}
}
}
} while (isChanged); //増加路が見つからなくなるまで続ける
return result;
}
}