最長パス
最長のパスを求める方法は、 IPに定式化する方法が一般的なようだ。実用的にはその方がいいだろうが、作るのはちょっと面倒だ。以下に全探索する方法を紹介する。計算量は指数オーダなので、複雑なものはできない。
/// <summary>
/// グラフ上の最長路のホップ数を返す
/// (有向アークが含まれていてはいけない)
/// </summary>
public static int GetLongestPath<TN, TA>(Graph<TN, TA> grp)
{
foreach (ArcTag<TN, TA> at in grp.ArcTags)
if (at.IsEnable && at.IsDirected) throw new ApplicationException("Exist directed arc");
bool[] unchk = new bool[grp.ArcsCount]; // 未チェックのアークか
int[] degree = new int[grp.NodesCount]; // ノードの次数
foreach (ArcTag<TN, TA> at in grp.ArcTags)
{
if (unchk[at.Index] = (at.IsEnable && at.LeftTag.IsEnable && at.RightTag.IsEnable))
{
++degree[at.LeftID];
++degree[at.RightID];
}
}
List<GLPData> dt = new List<GLPData>(); // 次数が2のノードを縮約した後のリンクの集合
GLPData gd = new GLPData();
Dictionary<int, int> dic = new Dictionary<int, int>(); // ノードIndex→縮約後のノードIndex
// 縮約する
while (true)
{
ArcTag<TN, TA> fnd = null, fst = null;
for (int i = 0; i < grp.ArcsCount; ++i)
{
if (!unchk[i]) continue;
ArcTag<TN, TA> at = grp.ArcTag(i);
if (fst == null) fst = at;
if (degree[at.LeftID] == 2 && degree[at.RightID] == 2) continue;
fnd = at;
break;
}
if (fnd == null && (fnd = fst) == null) break;
// 縮約後のノード数が31以上なら求められない
if (dt.Count >= 31) throw new ApplicationException("Too complex");
NodeTag<TN, TA> nt = degree[fnd.RightID] != 2 ? fnd.RightTag : fnd.LeftTag;
gd.len = 0;
gd.node1 = GetIndex(dic, nt.Index);
while (true)
{
unchk[fnd.Index] = false;
++gd.len;
nt = grp.NodeTag(fnd.AnotherID(nt.Index));
if (degree[nt.Index] != 2) break;
foreach (ArcTag<TN, TA> act in nt.InArcTags)
{
if (act.IsEnable && act != fnd)
{
fnd = act;
break;
}
}
if (!unchk[fnd.Index]) break;
}
gd.node2 = GetIndex(dic, nt.Index);
dt.Add(gd);
}
if (dt.Count == 0) return 0;
int[] con = new int[dic.Count]; // 縮約後のノードの次数
int[] src = new int[dic.Count]; // 0:非対象、1:未チェック、2:チェック済み
bool[] bUse = new bool[dt.Count]; // 縮約後のリンクのON/OFF
int max = 0, cur = 0, odd = 0;
uint n = 1U << dt.Count;
// リンクのON/OFFの全組合せをチェック
for (uint i = 0; i < n - 1; ++i)
{
int k = (int)Math.Log((i | ~(~i - 1)) - i, 2);
gd = dt[k];
int sgn = (bUse[k] = !bUse[k]) ? 1 : -1;
cur += sgn * gd.len;
odd += (con[gd.node1] += sgn) % 2 == 0 ? -1 : 1;
odd += (con[gd.node2] += sgn) % 2 == 0 ? -1 : 1;
// 次数が奇数の数が3以上なら一筆書きでない
if (odd > 2 || cur <= max) continue;
// 連結性のチェック
bool bFound = false;
for (int h = 0; h < src.Length; ++h)
{
src[h] = con[h] == 0 ? 0 : (bFound ? 1 : 2);
if (src[h] > 0) bFound = true;
}
while (bFound)
{
bFound = false;
foreach (GLPData gld in dt)
{
if (src[gld.node1] + src[gld.node2] == 3)
{
bFound = true;
src[gld.node1] = src[gld.node2] = 2;
}
}
}
for (int h = 0; h < src.Length; ++h)
if (bFound = src[h] == 1) break;
if (!bFound) max = cur;
}
return max;
}
private struct GLPData
{
public int len;
public int node1;
public int node2;
}
private static int GetIndex(Dictionary<int, int> dic, int i)
{
int k;
if (!dic.TryGetValue(i, out k))
dic.Add(i, k = dic.Count);
return k;
}