{"cells":[{"cell_type":"markdown","metadata":{"id":"9xoK4OIjIfZA"},"source":["#計算機程式設計二\n","#第十五週上課內容\n","## Problem Solving Using C++"]},{"cell_type":"markdown","source":["## 為 Water Jugs Problem 鋪路\n","\n","請先參考 jugs.pptx 投影片的說明，認識一下我們想要解決的問題。在開始解 Water Jugs Problem 之前，我們必須先介紹一些 C++ Standard Library 裡面的幾個常用的 containers 的基本用法。\n"],"metadata":{"id":"fAO6f7xoZZ2o"}},{"cell_type":"markdown","source":["### 範例 `map_example.cpp`\n","\n","這個範例和之前的WordCount 的問題相同，要計算輸入的英文字出現的次數。\n","適合用來當作 `map` 的練習。\n","\n","`map<string, int> WordFreq;` 產生了一個物件 `WordFreq`，是由 `string` 對應到 `int` 的一種資料儲存方式。尖括號裡面第一個參數 `string` 代表 key，第二個參數 `int` 是 value，我們可以用 key 來查詢對應的 value 是多少。`map` 可以使用跟陣列一樣的 `[]` 運算符號。在 C++ 稱作 map (因為是做從 key 到 value 的 mapping)，不過由於作用很像字典，在其他語言也稱作 dictionary。\n","\n","用 `while (cin >> str)` 讀取鍵盤輸入的資料，暫存到字串變數 `str` 裡面，然後用 `str` 當作 key，來查詢 `WordFreq` 這個 map 裡面是否已經有儲存過相同資料。如果有，就用 `WordFreq[str]++;` 讓次數加一，如果沒出現過，就設定 `WordFreq[str] = 1;`。\n","如何判斷 map 裡面是否已經有相同資料？第一種方式是用 `find()` 函數。\n","```c++\n","\titerator find (const key_type& k);\n","```\n","如果找不到相同 key 的資料，`find()` 會傳回一個指向 map 結尾的 iterator (先想成是 pointer)，所以我們只需要拿來和 `WordFreq.cend()` 或 `WordFreq.end()` 比較，就能判斷是否找到了對應的資料。\n","```c++\n","        if (found == WordFreq.cend()) {\n","            // a new word, initialize its count as 1\n","            WordFreq[str] = 1;\n","        } else {\n","            // an existing word; increase its frequency by 1\n","            WordFreq[str]++;\n","        }\n","```\n","\n","判斷是否已在 map 裡面，除了用 `find()` 函數之外，第二種方式是直接用 `count()`，用法如下\n","```c++\n","        if (WordFreq.count(str)==0) {\n","            // a new word, initialize its count as 1\n","            WordFreq[str] = 1;\n","        } else {\n","            // an existing word; increase its frequency by 1\n","            WordFreq[str]++;\n","        }\n","```\n","map 的 `count()` member function，只會傳回兩種值，`0` 代表要找的 key 不在 map 裡，`1` 則表示已經在 map 裡。\n","\n","當資料都已經用迴圈讀取完畢，都放入 map 並且累計次數之後，剩下的事情就只需要把 map 的內容，依序取出並顯示到螢幕上。由於 map 本來就會自己依照 key 的大小排序，所以如果 key 是 `string`，就會用英文字母的順序來排。我們用 `for` 迴圈，把 map 裡面每一筆資料取出，每一筆資料可以看成 `pair` 的形式，`.first` 就是 key，`.second` 則是 value。\n","\n","底下是完整的程式碼。"],"metadata":{"id":"yCr3OBPsZgKg"}},{"cell_type":"code","source":["%%writefile map_example.cpp\n","#include <iostream>\n","#include <algorithm>\n","#include <map>\n","using namespace std;\n","int main()\n","{\n","  map<string, int> WordCount;\n","  WordCount[\"John\"] = 100;\n","  WordCount[\"Bob\"] = 300;\n","  auto it = WordCount.find(\"John\");\n","  if (it != WordCount.end()) {\n","    cout << (*it).first << \" \" << (*it).second << \" found!\\n\";\n","  }\n","\n","  if ( WordCount.find(\"Marry\") == WordCount.end()) {\n","    cout << \"Not found.\\n\";\n","  }\n","\n","  cout << WordCount.count(\"John\") << \" \" << WordCount.count(\"Marry\") << \"\\n\";\n","  \n","  for (const auto& v: WordCount) {\n","    cout << v.first << \" \" << v.second << \"\\n\";\n","  }\n","\n","}\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"hyGDb4H2Pebz","executionInfo":{"status":"ok","timestamp":1671536506280,"user_tz":-480,"elapsed":303,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"1338d2fc-6fef-4de2-9cbe-166b7914e748"},"execution_count":13,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting map_example.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ map_example.cpp -o map_example -std=c++1z\n","./map_example"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"ywPQyFfBQvIw","executionInfo":{"status":"ok","timestamp":1671536509887,"user_tz":-480,"elapsed":745,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"343e85f8-87e0-42dd-cf4e-bc35ab62b337"},"execution_count":14,"outputs":[{"output_type":"stream","name":"stdout","text":["John 100 found!\n","Not found.\n","1 0\n","Bob 300\n","John 100\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":14}]},{"cell_type":"code","source":["%%writefile map_test.cpp\n","#include <iostream>\n","#include <algorithm>\n","#include <map>\n","using namespace std;\n","int main()\n","{\n","    // create a map (a dictionary) that maps a string to an integer\n","    // map: key->value where\n","    // key: string, value: int\n","    map<string, int> WordFreq;\n","\n","    string str;\n","    while (cin >> str) {\n","        // use the idiom of finding an element in a container\n","        auto found = WordFreq.find(str);\n","        if (found == WordFreq.cend()) {\n","            // a new word, initialize its count as 1\n","            WordFreq[str] = 1;\n","        } else {\n","            // an existing word; increase its frequency by 1\n","            WordFreq[str]++;\n","        }\n","    }\n","\n","    // iterate through the data in the map\n","    // the data are sorted by the keys\n","    // .first is the key and .second is the value\n","    for (auto it=WordFreq.cbegin(); it!=WordFreq.cend(); ++it) {\n","        cout << (*it).first << \": \" << (*it).second << '\\n';\n","    }\n","}"],"metadata":{"id":"zIy0hOCTZlVS"},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":["最後面的迴圈，可以改成 range-based for loop，會更簡潔。\n","\n","```c++\n","    for (auto w : WordFreq) {\n","        cout << w.first << \": \" << w.second << '\\n';\n","    }\n","```"],"metadata":{"id":"zTY18LqdZwS6"}},{"cell_type":"markdown","source":["### 範例 `vector.example.cpp`\n","\n","請看底下的程式碼。先產生物件 `vector<int> vec;`。然後搭配 `cin` 和 `while` 迴圈讀取資料，用 `push_back()` 函數來將資料放入 `vec` 後面。\n","資料讀完之後，接來可以用至少四種不同迴圈寫法，把 `vector` 裡面存放的資料取出來。\n","- 第一種是用迴圈跑過所有的 index，然後用陣列的寫法，`vec[i]` 取出對應編號的元素。\n","- 第二種則是用 range-based for loop，在這個例子中，因為只是單純的要把資料取出來顯示，這應該是最合適的寫法，不需要擔心是否會超出 `vector` 範圍。\n","- 第三種是用 `for_each`，必須要先 `#include <algorithm>`才能使用。通常 `for_each` 會搭配 lambda 函數使用，可以做顯示之外，更多複雜的操作。因為這個例子只是要顯示資料，我們需要的 lambda 是\n","```c++\n","\t[](const int v){cout << v << \" \"; }\n","```\n","暫時可以把 `[]` 想成是一個沒名字的函數，接收一個參數 `v`，函數內做的事情是把 `v` 印出來。 `for_each` 會拿這個函數，依序套用在 vector 裡面的每一個元素 (用 `vec.cbegin()` 和 `vec.cend()` 來指定要處理的範圍)，所以 vector 裡面的每個元素，會輪流扮演參數 `v`，然後被 `cout` 顯示到螢幕上。\n","\n","- 第四種，把 iterator 當指標來用。\n","\n","這個範例的第二段，主要是介紹如何用 `sort` `random_shuffle` `any_of` `none_of` `all_of`\n","這些函數來處理 vector 資料。 `sort` 和 `random_shuffle` 的用法應該很單純，看了範例就會知道。至於 `any_of` `none_of` `all_of`，則都是要搭配額外的函數或是 lambda 函數，當作第三個參數傳入 (把函數當作參數傳入 `any_of` 或 `none_of` 或 `all_of`)。當作參數傳入的函數，會套用在 vector 的每個元素上面，依據條件判斷的結果，傳回 `true` 或 `false`，然後如果是 `any_of` ，只要任一元素傳回 `true`，整個 vector 對於 `any_of` 得到的結果就會是 `true`，如果是 `none_of`，則是每個元素都是 `false`，整個 vecotr 對於 `none_of` 才會是 `true`。至於 `all_of` 則是每個元素都必須是 `true` 整個 vector 對於 `all_of` 才會是 `true`。\n","\n"],"metadata":{"id":"hGtnRuU3Z48m"}},{"cell_type":"code","source":["%%writefile vector_example.cpp\n","#include <iostream>\n","#include <vector>\n","#include <algorithm>\n","\n","using namespace std;\n","int main()\n","{\n","    vector<int> vec;\n","    int i;\n","\n","    // We can grow a vector incrementally using push_back().\n","    // To stop cin, type Ctrl-Z+Enter at the beginning of a line.\n","    cout << \"Enter some integers \";\n","    cout << \"and type Ctrl-Z + Return at the beginning of a newline.\\n\";\n","    while (cin >> i) {\n","        vec.push_back(i);\n","    }\n","\n","    // There are several way to iterate through a vector.\n","    // They can be treated as idioms.\n","    // 1. Access the elements by indexes and []\n","    for (unsigned int i=0; i<vec.size(); i++) {\n","        cout << vec[i] << ' ';\n","    }\n","    cout << endl;\n","\n","    // 2. Use C++11 range-based for loops.\n","    for (const auto& v : vec) {\n","        cout << v << ' ';\n","    }\n","    cout << endl;\n","\n","    // 3. Use for_each (need to include <algorithm>) and C++11 lambda functions\n","    for_each(vec.cbegin(), vec.cend(),\n","             [](const int v){cout << v << \" \"; }\n","             );\n","    cout << endl;\n","\n","    // 4. Use an iterator\n","    for (auto it=vec.cbegin(); it!=vec.cend(); ++it) {\n","        cout << *it << ' ';\n","    }\n","    cout << endl;\n","\n","    // Other types of vectors\n","    vector<string> heroes{\"Ironman\", \"Batman\", \"Superman\", \"Spiderman\", \"Thor\", \"Deadpool\"};\n","    for (auto s : heroes)\n","        cout << s << ' ';\n","    cout << endl;\n","\n","    cout << \"Sorted: \\n\";\n","    sort(heroes.begin(), heroes.end());\n","    for (auto s : heroes)\n","        cout << s << ' ';\n","    cout << endl;\n","\n","    cout << \"Shuffle: \\n\";\n","    random_shuffle(heroes.begin(), heroes.end());\n","    for (auto s : heroes)\n","        cout << s << ' ';\n","    cout << endl;\n","\n","    // functions in <algorithm>\n","    // all_of, none_of, any_of\n","    if ( any_of(heroes.cbegin(), heroes.cend(), [](string s){ return s.length()>8; }) )\n","        cout << \"Some name has more than eight letters.\\n\";\n","    else\n","        cout << \"false\\n\";\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"-e8ZHDU4ZzdL","executionInfo":{"status":"ok","timestamp":1671537465281,"user_tz":-480,"elapsed":300,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"3a2aefa9-96ce-4b31-b7b3-a6ee40860ae3"},"execution_count":23,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting vector_example.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ vector_example.cpp -o vector_example -std=c++1z\n","./vector_example"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"eouw4encWqlo","executionInfo":{"status":"ok","timestamp":1671537476765,"user_tz":-480,"elapsed":7113,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"3aa47bcf-f5f7-45b1-9a29-bfdc141247ae"},"execution_count":24,"outputs":[{"output_type":"stream","name":"stdout","text":["Enter some integers and type Ctrl-Z + Return at the beginning of a newline.\n","1 2 3 4 5\n","∂\n","1 2 3 4 5 \n","1 2 3 4 5 \n","1 2 3 4 5 \n","1 2 3 4 5 \n","Ironman Batman Superman Spiderman Thor Deadpool \n","Sorted: \n","Batman Deadpool Ironman Spiderman Superman Thor \n","Shuffle: \n","Superman Spiderman Deadpool Ironman Batman Thor \n","Some name has more than eight letters.\n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":24}]},{"cell_type":"markdown","source":["### 範例 `set_example.cpp`\n","\n","- 把資料放入 set，可以用 initializer list 的方式，在產生物件的時候，用 copy constructor 把資料複製到 set 裡面。\n","- 也可以用 `insert()` 函數，把新的資料放入 set 裡面。\n","- 在 set 裡面找資料，也是用 `find()` 函數。\n","\n","範例的後半段，示範如何用 `next_permutation` 來產生不同的排列，而且把結果放入 `set<vector<int>>`，set 裡面的每個元素都是一個由 int 構成的 vector，。\n","\n","範例的最後，則是示範如何用 `bitset` 來窮舉 powerset。\n","`bitset` 可以把整數轉成二進位表示法，我們可以透過查詢每個位元是 `0` 或 `1`，來決定是否要把對應到該位元的資料挑選出來。"],"metadata":{"id":"IGUZYIR5aFhw"}},{"cell_type":"code","source":["%%writefile set_example.cpp\n","#include <iostream>\n","#include <set>\n","#include <algorithm>\n","#include <vector>\n","#include <bitset>\n","using namespace std;  // otherwise we need to write std::set\n","\n","int main()\n","{\n","    set<int> S{1, 2, 3, 4};\n","\n","    // insert a new element into a set\n","    for (auto c : S)\n","        cout << c << ' ';\n","    cout << '\\n';\n","    cout << \"insert 5: \";\n","    S.insert(5);\n","    for (auto c : S)\n","        cout << c << ' ';\n","    cout << '\\n';\n","\n","    // insert an existing element into a set\n","    S.insert(1);\n","    cout << \"insert 1: \";\n","    for (auto c : S)\n","        cout << c << ' ';\n","    cout << '\\n';\n","\n","    // We use the following idiom to find an element in a set.\n","    auto x = S.find(3); // x is an iterator\n","    if (x != S.cend()) {\n","        cout << \"Found: \" << *x << '\\n';\n","    }\n","\n","\n","    // Generate all permutations and store them in a set.\n","    vector<int> v{1,2,3};\n","    set<vector<int>> T;\n","    do {\n","        T.insert(v);\n","    } while ( next_permutation(v.begin(), v.end()) );\n","\n","    for (auto v : T) {\n","      for (auto e : v) {\n","          cout << e << ' ';\n","      }\n","      cout << \"\\n\";\n","    }\n","\n","    // using bitset to generate power sets\n","    vector<string> vs{\"RED\",\"GREEN\",\"BLUE\"};\n","    for (int i=0; i<8; ++i) {\n","        bitset<3> b(i);\n","        for (int j=0; j<3; ++j) {\n","            if (b[j]==1) {\n","                cout << vs[j] << ' ';\n","            }\n","        }\n","        cout << '\\n';\n","    }\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"L2RakCPiaIhq","executionInfo":{"status":"ok","timestamp":1671538073539,"user_tz":-480,"elapsed":363,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"ae2d3369-ed57-4ee8-b2ac-7571537dc09b"},"execution_count":32,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting set_example.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ set_example.cpp -o set_example -std=c++1z\n","./set_example"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"_Vp3mEnmaQx4","executionInfo":{"status":"ok","timestamp":1671538032783,"user_tz":-480,"elapsed":768,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"398c2e20-9195-4cfe-8bc5-8b77df936cac"},"execution_count":31,"outputs":[{"output_type":"stream","name":"stdout","text":["1 2 3 4 \n","insert 5: 1 2 3 4 5 \n","insert 1: 1 2 3 4 5 \n","Found: 3\n","1 2 3 \n","1 3 2 \n","2 1 3 \n","2 3 1 \n","3 1 2 \n","3 2 1 \n","\n","RED \n","GREEN \n","RED GREEN \n","BLUE \n","RED BLUE \n","GREEN BLUE \n","RED GREEN BLUE \n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":31}]},{"cell_type":"markdown","source":["## 進入正題: 寫程式解出 Water Jugs Problem\n","\n","需要一個基本的資料結構，用來描述每個水桶在某個時刻的狀態，`using State = vector<int>;`。\n","例如 {3, 0} 代表第一個水桶裡面有三加侖的水，另一個則是空的。\n","\n","接下來我們要定義 `class Pouring`，用來描述 Water Jugs Problem 的倒水過程。\n","`class Pouring` 包含幾個 private members:\n","```c++\n","    vector<int> _capacities; // {3, 5}\n","    set<State> _explored;\n","    set<list<State>> _paths;\n","    set<list<State>> _solutions;\n","```\n","- `_capacities` 用來記錄每個水桶的容量上限。\n","- `_explored` 用來標記已經出現過的狀態，避免重複。\n","- `_paths` 則是一堆 `list<State>`的集合，`list<State>` 構成了一條路徑(一種解法、一種倒水順序)，我們把全部的路徑都放在 `_paths` 裡面，解題的過程中，可以取出任意一條路徑，然後試著往下走。\n","- `_solutions` 跟 `_paths` 一樣，不過只儲存能夠達成目標的解法。\n","\n","`class Pouring` 的 constructor 只需要把 `_capacities` 設定好。\n","\n","按照題目定義，我們可以做三種操作：`Empty`、`Fill`、`Pour`，把水桶的水倒光、把水桶的水裝滿、把某個水桶的水倒到另一個水桶。我們必須實做出這三項操作。\n","```c++\n","    State Empty(State s, int jug_no);\n","    State Fill(State s, int jug_no);\n","    State Pour(State s, int from, int to);\n","```\n","\n","再來是 `set<State> extend(State s)` 函數，傳入的參數是某個狀態，然後依據狀態，用上述的三種操作，把狀態改變，然後把產生的新狀態，通通存入 `set<State>` 裡面。\n","\n","最關鍵的函數是 `void solve(int target, int steps)`。其中 `target` 是我們想要達到的目標，某個桶子裡面有指定的水量。 `steps` 則是希望在多少步驟之內解出答案。\n","主要的想法如下 (從 jugs.pptx 節錄出來)\n","1. For each path in the set of candidate paths\n"," 1 Extract the last state of that path and  add it into the set of explored states\n","   2 Extend the last state to get a set of next states that have not been visited yet\n","   3 For each of the new next states, append it to the end of the current path and therefore obtain a new path\n","\n","2. If the new state is the target, we are done; \n","\n","3. Otherwise , add the new path into the set of candidate paths.\n","\n","對照程式碼會更清楚上述的步驟如何實現。\n","\n","\n","\n","完整的程式碼如下:"],"metadata":{"id":"GNjiNVP-aN5o"}},{"cell_type":"code","source":["%%writefile jugs.cpp\n","#include <iostream>\n","#include <vector>\n","#include <algorithm>\n","#include <set>\n","#include <list>\n","#include <iterator>\n","#include <string>\n","#include <sstream>\n","using namespace std;\n","using State = vector<int>;\n","class Pouring\n","{\n","private:\n","    vector<int> _capacities; // {3, 5}\n","    set<State> _explored;\n","    set<list<State>> _paths;\n","    set<list<State>> _solutions;\n","\n","public:\n","    Pouring(vector<int> cp): _capacities{cp} { }\n","    State Empty(State s, int jug_no)\n","    {\n","        s[jug_no] = 0;\n","        return s;\n","    }\n","    State Fill(State s, int jug_no)\n","    {\n","        s[jug_no] = _capacities[jug_no];\n","        return s;\n","    }\n","    State Pour(State s, int from, int to)\n","    {\n","        State t = s;\n","        int diff = _capacities[to]-s[to];\n","        if (diff < s[from]) {\n","            t[to] = _capacities[to];\n","            t[from] = s[from]-diff;\n","        } else {\n","            t[from] = 0;\n","            t[to] = s[to] + s[from];\n","        }\n","        return t;\n","    }\n","    set<State> extend(State s)\n","    {\n","        set<State> SS;\n","        for (int i=0; i<_capacities.size(); ++i) {\n","            SS.insert(Empty(s, i));\n","            SS.insert(Fill(s, i));\n","            for (int j=0; j<_capacities.size(); ++j) {\n","                if (i!=j)\n","                    SS.insert(Pour(s, i, j));\n","            }\n","        }\n","        return SS;\n","    }\n","\n","    void show_state(State s)\n","    {\n","        for (auto i : s)\n","            cout << i << \", \" ;\n","        cout << \"->\";\n","    }\n","    bool found(State s, int target)\n","    {\n","        for (auto t : s) {\n","            if (t==target) return true;\n","        }\n","        return false;\n","        // return any_of(s.cbegin(), s.cend(), [=](int v){ return target==v;});\n","    }\n","    void solve(int target, int steps)\n","    {\n","        list<State> initialPath;\n","        initialPath.push_back(State(_capacities.size()));\n","        _paths.insert(initialPath);\n","\n","        while (steps > 0) {\n","            set<list<State>> newPaths;\n","            set<list<State>> oldPaths;\n","\n","            for (auto p : _paths) {\n","                _explored.insert(p.back());\n","                auto nextStates = extend(p.back());\n","                for (auto s : nextStates) {\n","                    if (found(s, target)) {\n","                        auto np = p;\n","                        np.push_back(s);\n","                        _solutions.insert(np);\n","                    } else {\n","                    auto search = _explored.find(s);\n","                    if (search == _explored.cend()) {\n","                        auto np = p;\n","                        np.push_back(s);\n","                        newPaths.insert(np);\n","                    }\n","                    }\n","                }\n","                oldPaths.insert(p);\n","            }\n","\n","            for (auto p : oldPaths) {\n","                _paths.erase(p);\n","            }\n","            for (auto p : newPaths) {\n","                _paths.insert(p);\n","            }\n","            --steps;\n","        }\n","\n","    }\n","    void show_solutions()\n","    {\n","        for (auto path : _solutions) {\n","            for (auto state : path) {\n","                show_state(state);\n","            }\n","            cout << \"\\n\";\n","        }\n","    }\n","};\n","\n","int main()\n","{\n","    vector<int> jugs = {3, 5};\n","    Pouring problem(jugs);\n","    problem.solve(4, 6);\n","    problem.show_solutions();\n","}\n"],"metadata":{"id":"UI0QkMxQaT3r"},"execution_count":null,"outputs":[]},{"cell_type":"code","source":["%%writefile jugs.cpp\n","#include <iostream>\n","#include <vector>\n","#include <list>\n","#include <set>\n","#include <algorithm>\n","\n","using namespace std;\n","\n","using State = vector<int>;\n","using Path = list<State>;\n","\n","class Pouring\n","{\n"," public:\n","  Pouring(vector<int> v): _capacities{v} \n","  {\n","  }\n","\n","  State Empty(int idx, State s)\n","  {\n","    s[idx] = 0;\n","    return s;\n","  }\n","\n","  State Fill(int idx, State s)\n","  {\n","    s[idx] = _capacities[idx];\n","    return s;\n","  }\n","\n","  State Pour(int src, int tgt, State s)\n","  {\n","    int diff = _capacities[tgt] - s[tgt];\n","    if (s[src] > diff) {\n","      s[tgt] = _capacities[tgt];\n","      s[src] = s[src] - diff;\n","    } else {\n","      s[tgt] = s[tgt] + s[src];\n","      s[src] = 0;\n","    }\n","    return s;\n","  }\n","\n","  set<State> extend(State s) {\n","    set<State> S;\n","    for (int i=0; i<_capacities.size(); ++i) {\n","      S.insert(Empty(i, s));\n","      S.insert(Fill(i, s));\n","      for (int j=0; j<_capacities.size(); ++j) {\n","        if (i!=j) {\n","          S.insert(Pour(i, j, s));\n","        }\n","      }\n","    }\n","    return S;\n","  }\n","  \n","  bool found(State s, int goal) {\n","    return any_of(s.cbegin(), s.cend(), [goal](int x) { return x==goal;});\n","  }\n","\n","  void solve(int goal, int steps) {\n","    Path initialPath;\n","    initialPath.push_back(State(_capacities.size()));\n","    _paths.insert(initialPath);\n","\n","    while (steps > 0) {\n","      set<Path> newPaths;\n","\n","      for (const auto & p: _paths) {\n","        _explored.insert(p.back()); // p.back() is a State   p is a list of States\n","        auto nextStates = extend(p.back());\n","        for (const auto &s : nextStates) {\n","          if (found(s, goal)) {\n","            auto np = p;\n","            np.push_back(s);\n","            _solutions.insert(np);\n","          } else {\n","              if (_explored.find(s) == _explored.cend()) {\n","                auto np = p;\n","                np.push_back(s);\n","                newPaths.insert(np);\n","              }\n","          }\n","        }\n","      }\n","      _paths = newPaths;\n","      --steps;\n","    }\n","  }\n"," \n","  void show_solutions() {\n","      for (const auto& p: _solutions) {\n","          for (const auto& s: p) {\n","              cout << \"(\";\n","              for (const auto& x: s) {\n","                cout << x << \", \";\n","              }\n","              cout << \") -> \";\n","          }\n","          cout << \"\\n\";\n","      }\n","  }\n","  \n"," \n"," private:\n","  vector<int> _capacities;\n","  set<Path> _solutions;\n","  set<Path> _paths;\n","  set<State> _explored;\n","};\n","\n","int main()\n","{\n","  vector<int> jugs{3, 4, 7};\n","  Pouring problem{jugs};\n","  problem.solve(5, 6);\n","  problem.show_solutions();\n","}"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"L9wI8z7TgEcH","executionInfo":{"status":"ok","timestamp":1671542696545,"user_tz":-480,"elapsed":308,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"eee2ad3e-f6b0-4bc5-dc50-4bbf5e1cfe3e"},"execution_count":71,"outputs":[{"output_type":"stream","name":"stdout","text":["Overwriting jugs.cpp\n"]}]},{"cell_type":"code","source":["%%shell\n","g++ jugs.cpp -o jugs -std=c++1z\n","./jugs"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"eEVUyXh9jj68","executionInfo":{"status":"ok","timestamp":1671542703042,"user_tz":-480,"elapsed":1491,"user":{"displayName":"HT Chen","userId":"17748361917871513601"}},"outputId":"80262d93-5a5c-41b6-c05b-871cfee5c2a4"},"execution_count":72,"outputs":[{"output_type":"stream","name":"stdout","text":["(0, 0, 0, ) -> (0, 0, 7, ) -> (0, 4, 7, ) -> (3, 1, 7, ) -> (0, 1, 7, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (0, 4, 7, ) -> (3, 1, 7, ) -> (0, 1, 7, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (0, 4, 7, ) -> (3, 4, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (0, 4, 7, ) -> (3, 4, 4, ) -> (0, 4, 4, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 0, 4, ) -> (3, 0, 1, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 3, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 3, 4, ) -> (0, 4, 4, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 3, 4, ) -> (3, 3, 1, ) -> (2, 4, 1, ) -> (2, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (0, 3, 4, ) -> (3, 3, 1, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (3, 4, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 0, 7, ) -> (3, 0, 4, ) -> (3, 4, 4, ) -> (0, 4, 4, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (0, 1, 7, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (0, 1, 7, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (0, 1, 4, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 1, 4, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 4, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (0, 4, 4, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (3, 0, 1, ) -> (0, 0, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (3, 0, 1, ) -> (0, 3, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (3, 0, 1, ) -> (3, 4, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 0, 4, ) -> (3, 0, 1, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (0, 4, 7, ) -> (3, 1, 7, ) -> (0, 1, 7, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (0, 1, 0, ) -> (0, 0, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (0, 1, 0, ) -> (0, 1, 7, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (0, 1, 0, ) -> (1, 0, 0, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (0, 1, 3, ) -> (0, 1, 7, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (0, 1, 3, ) -> (1, 0, 3, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (3, 0, 1, ) -> (0, 0, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (3, 0, 1, ) -> (0, 3, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (3, 0, 1, ) -> (3, 4, 1, ) -> (0, 4, 1, ) -> (0, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (3, 0, 1, ) -> (3, 4, 1, ) -> (3, 0, 5, ) -> \n","(0, 0, 0, ) -> (0, 4, 0, ) -> (3, 1, 0, ) -> (3, 1, 7, ) -> (0, 1, 7, ) -> (1, 0, 7, ) -> (3, 0, 5, ) -> \n"]},{"output_type":"execute_result","data":{"text/plain":[]},"metadata":{},"execution_count":72}]}],"metadata":{"colab":{"provenance":[{"file_id":"1AB3RNlLfkZBSZKu6_Wh20CkIpbxfJIve","timestamp":1663461789208}],"authorship_tag":"ABX9TyPma29TYYpW2jXOXRgXlbxW"},"gpuClass":"premium","kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"}},"nbformat":4,"nbformat_minor":0}