Problem:
自転車でダイエット

A さんは甘いものが大好きですが、最近は奥さんからダイエットするように言われています。ある日、A さんが自宅から市役所に出かけるとき、奥さんは自転車で行くことを勧めました。そこでA さんはしぶしぶ自転車に乗って出かけましたが、甘い物の好きな A さんは、途中にあるケーキ屋さんに立ち寄りケーキの食べ歩きをしようと思いつきました。

自転車をこげば走行距離に応じてカロリーが消費されますが、ケーキを食べればその分カロリーが摂取されます。正味の消費カロリーは、自転車をこいで消費したカロリーからケーキを食べて摂取したカロリーを引いた値になります。したがって、正味の消費カロリーが 0 より小さくなることもあります。

ケーキ屋さんでケーキを買ったら、 Aさんはその場でケーキを全部食べてしまいます。 Aさんがすべてのケーキ屋さんに立ち寄るとは限りませんが、ケーキ屋さんが存在する地点を通るときには、必ず立ち寄ってケーキを 1 つ買って食べるものとします。ただし、同じケーキ屋さんの前を何度も通るのはさすがに気がひけるので、各ケーキ屋さんに訪れられるのは 1 回だけとします。また、目的地の市役所をいったん通り過ぎてからケーキ屋さんに立ち寄り、その後で市役所に戻って用を済ませてもよいものとし、ケーキ屋さん以外は何度訪れても良いものとします。

A さんの自宅から市役所までの地図情報と、その途中にあるケーキ屋さんで食べられるケーキのカロリーの一覧、単位距離の走行による消費カロリーを入力とし、自宅を出発してから市役所に入るまでの正味の消費カロリーの最小値を出力するプログラムを作成してください。

地図には、A さんの自宅と市役所、ケーキ屋さんとランドマーク(目印)になる建物が書かれています。地図を表す入力データは、A さんの自宅、市役所、ケーキ屋さんとランドマークの各地点の間をつなぐ道があるとき、2 つの地点を表す記号とその間の距離からなる行を含みます。たとえば、5 番目のケーキ屋さんと 3 番目のランドマークの間の距離が 10 なら、入力データは以下のような行を含みます。

C5 L3 10

このようにケーキ屋さんには C、ランドマークには L を番号の前につけて表します。また、A さんの自宅は H、市役所は D で表します。入力データに 2 つの地点とその間の距離が与えられているなら、2 地点の間をどちら向きにも進めます。たとえば、上の例ではケーキ屋さんからランドマークへも、その逆向きへも進むことができます。また、自宅から市役所までは必ずたどり着けるものとします。それ以外に与えられる入力データは、ケーキ屋さんの数 m、ランドマークの数 n、単位距離あたりの消費カロリー k、 1 番目のケーキ屋さんから m 番目のケーキ屋さんまでのそれぞれで買えるケーキのカロリーを表す m 個のデータ、距離のデータの総数 d です。

Input

複数のデータセットの並びが入力として与えられます。入力の終わりは四つの 0 の行で示されます。
各データセットは以下の形式で与えられます。

m n k d
c1 c2 ... cm
s1 t1 e1
s2 t2 e2
:
sd td ed

1 行目にケーキ屋さんの数 m (1 ≤ m ≤ 6)、 ランドマークの数 n (1 ≤ n ≤ 100) 、単位距離あたりの消費カロリー k (1 ≤ k ≤ 5)、距離のデータの総数 d (5 ≤ d ≤ 256) が与えられます。

2 行目に各ケーキ屋で買うケーキのカロリー ci (1 ≤ ci ≤ 100) が与えられます。

続く d 行に i 番目の 2 つの地点間の距離データ si, ti, ei (1 ≤ ei ≤ 20) が与えられます。

データセットの数は 100 を超えません。

Output

入力データセットごとに、全体の消費カロリーの最小値を１行に出力します。

Sample Input

1 1 2 5
35
H L1 5
C1 D 6
C1 H 12
L1 D 10
C1 L1 20
2 1 4 6
100 70
H L1 5
C1 L1 12
C1 D 11
C2 L1 7
C2 D 15
L1 D 8
0 0 0 0

Output for the Sample Input

1
-2

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace _0224
{
    class Program
    {
        public const string START = "H";
        public const string GOAL = "D";
        public const string CAKESHOP = "C";
        public const string LANDMARK = "L";

        public static Dictionary<string, Node> Adj { get; set; }
        public class Node
        {
            public List<KeyValuePair<string, int>> Vertex { get; internal set; }
            public Node() { Vertex = new List<KeyValuePair<string, int>>(); }
        }

        public static Dictionary<string, int[]> distances;
        public static int[] CakeCals;

        static void Main(string[] args)
        {
            while (true)
            {
                int[] vs = RArInt();
                if (vs.Sum() == 0) break;
                Init(vs);
                Console.WriteLine(CalcMinCal());
            }
        }

        private static void Init(int[] vs)
        {
            int CakeShopCnt = vs[0];
            CakeCals = RArInt();

            Adj = new Dictionary<string, Node>();
            distances = new Dictionary<string, int[]>();

            Adj.Add(START, new Node());
            distances.Add(START, new int[1 << CakeShopCnt]);

            Adj.Add(GOAL, new Node());
            distances.Add(GOAL, new int[1 << CakeShopCnt]);

            for (int i = 0; i < vs[0]; i++)
            {
                string key = CAKESHOP + (i + 1).ToString();
                Adj.Add(key, new Node());
                distances.Add(key, new int[1 << CakeShopCnt]);
            }
            for (int i = 0; i < vs[1]; i++)
            {
                string key = LANDMARK + (i + 1).ToString();
                Adj.Add(key, new Node());
                distances.Add(key, new int[1 << CakeShopCnt]);

            }
            for (int i = 0; i < vs[3]; i++)
            {
                string[] items = RArSt();
                Adj[items[0]].Vertex.Add(new KeyValuePair<string, int>(items[1], int.Parse(items[2]) * vs[2]));
                Adj[items[1]].Vertex.Add(new KeyValuePair<string, int>(items[0], int.Parse(items[2]) * vs[2]));
            }
            foreach (var distance in distances)
            {
                for (int i = 0; i < distance.Value.Length; i++) distance.Value[i] = int.MaxValue;
            }
        }

        class Candidate : IComparable<Candidate>
        {
            public string V { get; set; }
            public int Distance { get; set; }
            public int Path { get; set; }
            public Candidate(string v, int d, int path) { Distance = d; V = v; Path = path; }
            public int CompareTo(Candidate other) { return -1 * Distance.CompareTo(other.Distance); }
        }
        private static int CalcMinCal()
        {
            distances[START][0] = 0;
            PriorityQueue<Candidate> pq = new PriorityQueue<Candidate>();
            pq.Enqueue(new Candidate("H", distances["H"][0], 0));

            while (pq.Count > 0)
            {
                Candidate c = pq.Dequeue();
                string v = c.V;
                int d = c.Distance;
                int p = c.Path;

                if (distances[v][p] < d) continue;

                foreach (KeyValuePair<string, int> nextV in Adj[v].Vertex)
                {
                    int nextP, nextD;
                    if (nextV.Key[0] == CAKESHOP[0])
                    {
                        int curCakeShop = int.Parse(nextV.Key[1].ToString()) - 1;
                        if ((p & (1 << curCakeShop)) > 0) continue;
                        nextP = p | (1 << curCakeShop);
                        nextD = distances[v][p] + nextV.Value - CakeCals[curCakeShop];
                    }
                    else
                    {
                        nextP = p;
                        nextD = distances[v][p] + nextV.Value;
                    }
                    if (distances[nextV.Key][nextP] > nextD)
                    {
                        distances[nextV.Key][nextP] = nextD;
                        pq.Enqueue(new Candidate(nextV.Key, nextD, nextP));
                    }
                }
            }
            return distances[GOAL].Min();
        }
        static string RSt() { return Console.ReadLine(); }
        static int RInt() { return int.Parse(Console.ReadLine().Trim()); }
        static long RLong() { return long.Parse(Console.ReadLine().Trim()); }
        static double RDouble() { return double.Parse(Console.ReadLine()); }
        static string[] RArSt(char sep = ' ') { return Console.ReadLine().Trim().Split(sep); }
        static int[] RArInt(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => int.Parse(e)); }
        static long[] RArLong(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => long.Parse(e)); }
        static double[] RArDouble(char sep = ' ') { return Array.ConvertAll(Console.ReadLine().Trim().Split(sep), e => double.Parse(e)); }
        static string WAr<T>(IEnumerable<T> array, string sep = " ") { return string.Join(sep, array.Select(x => x.ToString()).ToArray()); }
    }

    public class PriorityQueue<T> where T : IComparable<T>

    {

        private List<T> Buffer { get; set; }
        public int Count { get { return Buffer.Count; } }
        public PriorityQueue() { Buffer = new List<T>(); }
        public PriorityQueue(int capacity) { Buffer = new List<T>(capacity); }

        /// <summary>
        /// ヒープ化されている配列リストに新しい要素を追加する。
        /// </summary>
        public void Enqueue(T item)
        {
            int n = Buffer.Count;
            Buffer.Add(item);

            while (n != 0)
            {
                int i = (n - 1) / 2;
                if (Buffer[n].CompareTo(Buffer[i]) > 0)
                {
                    T tmp = Buffer[n]; Buffer[n] = Buffer[i]; Buffer[i] = tmp;
                }
                n = i;
            }
        }

        /// <summary>
        /// ヒープから最大値を取り出し、削除する。
        /// </summary>
        public T Dequeue()
        {
            T ret = Buffer[0];
            int n = Buffer.Count - 1;
            Buffer[0] = Buffer[n];
            Buffer.RemoveAt(n);

            for (int i = 0, j; (j = 2 * i + 1) < n;)
            {
                if ((j != n - 1) && (Buffer[j].CompareTo(Buffer[j + 1]) < 0))
                    j++;
                if (Buffer[i].CompareTo(Buffer[j]) < 0)
                {
                    T tmp = Buffer[j]; Buffer[j] = Buffer[i]; Buffer[i] = tmp;
                }
                i = j;
            }
            return ret;
        }

        /// <summary>
        /// ヒープから最大値を参照する。
        /// </summary>
        public T Peek()
        {
            if (Count == 0) throw new InvalidOperationException();
            return this.Buffer[0];
        }
    }
}