Guest User

Untitled

a guest
Dec 11th, 2025
276
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.69 KB | Source Code | 0 0
  1. #include "advent.hpp"
  2. #include <bit>
  3. #include <chrono>
  4. #include <format>
  5. #include <queue>
  6. #include <numeric>
  7. #include <utility>
  8.  
  9.  
  10. struct Machine {
  11.     i64 len = 0;
  12.     i64 target = 0;
  13.     vector<i64> buttons{};
  14.     vector<i64> joltage{};
  15. };
  16.  
  17. ostream& printBits(ostream& o, i64 v, i64 len) {
  18.     for (i64 i = len - 1; i >= 0; i--) {
  19.         o << ((v & (1LL << i)) ? '1' : '0');
  20.     }
  21.     return o;
  22. }
  23.  
  24. ostream& operator<<(ostream& os, const Machine& m) {
  25.     os << "(" << m.len << ", ";
  26.     printBits(os, m.target, m.len);
  27.     os << ", ";
  28.     bool first = true;
  29.     os << "[";
  30.     for (auto b : m.buttons) {
  31.         if (first) first = false; else os << ", ";
  32.         printBits(os, b, m.len);
  33.     }
  34.     os << "], ";
  35.     os << m.joltage;
  36.     os << ")";
  37.     return os;
  38. }
  39.  
  40. void iterateSets(int n, auto&& callback) {
  41.     uint64_t limit = 1ull << n;  // assumes n <= 63
  42.  
  43.     for (int k = 1; k <= n; ++k) {
  44.         uint64_t s = (1ull << k) - 1;
  45.         while (s < limit) {
  46.             if (!callback(s))
  47.                 return;
  48.             uint64_t c = s & -s;
  49.             uint64_t r = s + c;
  50.             s = (((r ^ s) >> 2) / c) | r;
  51.         }
  52.     }
  53. }
  54.  
  55. void iterateBits(uint64_t bits, auto&& callback) {
  56.     while (bits) {
  57.         uint64_t t = bits & -bits;
  58.         int r = std::countr_zero(t);
  59.         if (!callback(r))
  60.             return;
  61.         bits &= bits - 1;
  62.     }
  63. }
  64.  
  65. constexpr size_t LIMIT = 16;
  66. using Vec = array<int16_t, LIMIT>;
  67.  
  68. Vec operator+(const Vec& a, const Vec& b) {
  69.     Vec res;
  70.     for (size_t i = 0; i < LIMIT; i++)
  71.         res[i] = a[i] + b[i];
  72.     return res;
  73. }
  74. Vec operator-(const Vec& a, const Vec& b) {
  75.     Vec res;
  76.     for (size_t i = 0; i < LIMIT; i++)
  77.         res[i] = a[i] - b[i];
  78.     return res;
  79. }
  80. Vec operator*(const Vec& a, int16_t b) {
  81.     Vec res;
  82.     for (size_t i = 0; i < LIMIT; i++)
  83.         res[i] = a[i] * b;
  84.     return res;
  85. }
  86.  
  87. void fastSwap(Vec& a, Vec& b) {
  88.     auto tmp = a;
  89.     a = b;
  90.     b = tmp;
  91. }
  92.  
  93.  
  94. struct Solver {
  95.     const Machine& machine;
  96.     int buttons;
  97.     int jolts;
  98.     array<vector<int>, LIMIT> forward;
  99.     array<vector<int>, LIMIT> reverse;
  100.  
  101.     Solver(const Machine& machine) : machine(machine) {
  102.         buttons = machine.buttons.size();
  103.         jolts = machine.joltage.size();
  104.         assert(buttons + 1 <= forward.size());
  105.         assert(jolts + 1 <= reverse.size());
  106.         for (i64 b = 0; b < machine.buttons.size(); b++) {
  107.             for (i64 j = 0; j < machine.joltage.size(); j++) {
  108.                 if (machine.buttons[b] & (1ll << j)) {
  109.                     reverse[j].push_back(b);
  110.                     forward[b].push_back(j);
  111.                 }
  112.             }
  113.         }
  114.     }
  115.  
  116.     void simplify(Vec& v) {
  117.         int g = 0;
  118.         for (auto x : v)
  119.             g = gcd(g, x);
  120.         if (g > 1) {
  121.             for (auto& x : v)
  122.                 x /= g;
  123.         }
  124.         int first = 0;
  125.         for (; first < buttons; first++) {
  126.             if (v[first] != 0)
  127.                 break;
  128.         }
  129.         if (first < buttons && v[first] < 0) {
  130.             for (auto& x : v)
  131.                 x = -x;
  132.         }
  133.     }
  134.     static void pmat(const span<Vec>& rows) {
  135.         for (auto& row : rows) {
  136.             for (i64 i = 0; i < LIMIT; i++) {
  137.                 cout << format("{:4}", row[i]);
  138.             }
  139.             cout << endl;
  140.         }
  141.     }
  142.  
  143.     [[gnu::noinline]] pair<int, int> gaussian(vector<Vec>& rows, int limit = -1) {
  144.         bool doClean = limit >= 0;
  145.         // Run gaussian elimination
  146.         // Return a free variable and the maximum value for that variable
  147.         i64 n = rows.size();
  148.         i64 m = buttons;
  149.         for (i64 h = 0, k = 0; h < n && k < m; k++) {
  150.             // Find the kth pivot
  151.             i64 i_pivot = h;
  152.             for (i64 i = h; i < n; i++) {
  153.                 if (rows[i][k] != 0) {
  154.                     i_pivot = i;
  155.                     break;
  156.                 }
  157.             }
  158.             if (rows[i_pivot][k] == 0)
  159.                 continue; // No pivot in this column
  160.             // Swap rows
  161.             fastSwap(rows[h], rows[i_pivot]);
  162.             // For all rows below pivot
  163.             for (i64 i = 0; i < h; i++) {
  164.                 if (rows[i][k] != 0) {
  165.                     auto g = gcd(rows[h][k], rows[i][k]);
  166.                     rows[i] = rows[i] * (rows[h][k] / g) - rows[h] * (rows[i][k] / g);
  167.                 }
  168.             }
  169.             for (i64 i = i_pivot + 1; i < n; i++) {
  170.                 if (rows[i][k] != 0) {
  171.                     auto g = gcd(rows[h][k], rows[i][k]);
  172.                     rows[i] = rows[i] * (rows[h][k] / g) - rows[h] * (rows[i][k] / g);
  173.                 }
  174.             }
  175.             h++;
  176.         }
  177.         pair<int, int> candidate = {INT_MAX, INT_MAX};
  178.         array<int, 16> zeroInds{};
  179.         size_t removedZeros = 0;
  180.         for (i64 j = 0; j < rows.size(); j++) {
  181.             int count = 0;
  182.             int index = -1;
  183.             int numSame = 0;
  184.             int numGt = 0;
  185.             for (i64 b = 0; b < LIMIT - 1; b++) {
  186.                 if (rows[j][b] != 0) {
  187.                     count++;
  188.                     index = b;
  189.                     // Does it face same direction as the constant term
  190.                     numGt += rows[j][b] > 0;
  191.                     numSame += ((rows[j][b] >= 0) == (rows[j][LIMIT - 1] >= 0)) || (rows[j][LIMIT - 1] == 0);
  192.                 }
  193.             }
  194.             if (count == 0) {
  195.                 if (rows[j][LIMIT - 1] != 0)
  196.                     return {-1, -1}; // No solution
  197.                 rows[j] = rows.back();
  198.                 rows.pop_back();
  199.                 j--;
  200.                 continue;
  201.             }
  202.             if (numSame == 0) {
  203.                 // If none of our variables face the same direction as the constant term, no solution, as variables need to be positive
  204.                 return {-1, -1};
  205.             }
  206.             if (count == 1) {
  207.                 // We need integer solutions
  208.                 if (rows[j][LIMIT - 1] % rows[j][index] != 0)
  209.                     return {-1, -1};
  210.                 auto val = rows[j][LIMIT - 1] / rows[j][index];
  211.                 // We need >= 0 solutions
  212.                 if (val < 0)
  213.                     return {-1, -1};
  214.                 if (doClean) {
  215.                     rows[j] = rows.back();
  216.                     rows.pop_back();
  217.                     j--;
  218.                 }
  219.             } else {
  220.                 candidate = min(candidate, {limit, index});
  221.                
  222.                 if (numSame == count && (numGt == 0 || numGt == count)) {
  223.                     // All variables face the same direction as the constant term
  224.                     // We can compute a proper maximum
  225.                     for (i64 b = 0; b < LIMIT - 1; b++) {
  226.                         if (rows[j][b] != 0) {
  227.                             int coeff = rows[j][LIMIT - 1] / rows[j][b];
  228.  
  229.                             if (coeff == 0) {
  230.                                 for (i64 j2 = 0; j2 < rows.size(); j2++)
  231.                                     rows[j2][b] = 0;
  232.                                 assert(removedZeros < zeroInds.size());
  233.                                 zeroInds[removedZeros++] = b;
  234.                                 continue;
  235.                             }
  236.  
  237.                             candidate = min(candidate, {coeff, b});
  238.                         }
  239.                     }
  240.                 }
  241.             }
  242.         }
  243.         if (removedZeros) {
  244.             if (!doClean) {
  245.                 for (auto z = 0; z < removedZeros; z++) {
  246.                     i64 zeroInd = zeroInds[z];
  247.                     Vec newRow{};
  248.                     newRow[zeroInd] = 1;
  249.                     rows.push_back(newRow);
  250.                 }
  251.             }
  252.             return gaussian(rows, doClean);
  253.         }
  254.         for (auto& row : rows)
  255.             simplify(row);
  256.         if (candidate.first != INT_MAX) {
  257.             return {candidate.second, candidate.first};
  258.         }
  259.         return {-1, 0};
  260.     }
  261.  
  262.     array<vector<Vec>, 16> cache;
  263.     size_t cacheSize = 0;
  264.  
  265.     auto alloc() {
  266.         if (cacheSize == 0) {
  267.             return vector<Vec>{};
  268.         } else {
  269.             auto v = move(cache[--cacheSize]);
  270.             v.clear();
  271.             return v;
  272.         }
  273.     }
  274.     void dealloc(vector<Vec>&& v) {
  275.         cache[cacheSize++] = move(v);
  276.     }
  277.  
  278.    
  279.     bool feasibleSlow(vector<Vec>& rows, int freeVar, int maxVal, int limit) {
  280.         for (int val = 0; val <= maxVal; val++) {
  281.             vector cur = alloc();
  282.             cur.insert(cur.end(), rows.begin(), rows.end());
  283.             Vec row{};
  284.             row[freeVar] = 1;
  285.             row[LIMIT - 1] = val;
  286.             cur.push_back(row);
  287.             if (feasible(cur, limit)) {
  288.                 dealloc(move(cur));
  289.                 return true;
  290.             }
  291.             dealloc(move(cur));
  292.         }
  293.         return false;
  294.     }
  295.    
  296.  
  297.     bool feasible(vector<Vec>& rows, int limit) {
  298.         auto [freeVar, maxVal] = gaussian(rows, limit);
  299.         if (freeVar < 0) {
  300.             return maxVal == 0;
  301.         }
  302.         return feasibleSlow(rows, freeVar, maxVal, limit);
  303.     }
  304.  
  305.     int solve() {
  306.         vector<Vec> rows;
  307.         for (i64 j = 0; j < jolts; j++) {
  308.             Vec row{};
  309.             for (auto b : reverse[j]) row[b] = 1;
  310.             row[LIMIT - 1] = machine.joltage[j];
  311.             rows.push_back(row);
  312.         }
  313.         gaussian(rows);
  314.  
  315.         int val = 0;
  316.         for (auto j : machine.joltage)
  317.             val = max<int>(val, j);
  318.         for (; true; val++) {
  319.             auto cur = alloc();
  320.             cur.insert(cur.end(), rows.begin(), rows.end());
  321.             Vec row{};
  322.             for (i64 b = 0; b < buttons; b++) row[b] = 1;
  323.             row[LIMIT - 1] = val;
  324.             cur.push_back(row);
  325.             if (feasible(cur, val)) {
  326.                 dealloc(move(cur));
  327.                 return val;
  328.             }
  329.             dealloc(move(cur));
  330.         }
  331.     }
  332. };
  333.  
  334. int main() {
  335.     auto lines = readLines(cin);
  336.     vector<Machine> machines;
  337.     for (auto& l : lines) {
  338.         auto parts = split(l, " ");
  339.         Machine m;
  340.         m.len = parts[0].size() - 2;
  341.         for (i64 i = 1; i < parts[0].size() - 1; i++)
  342.             m.target |= (parts[0][i] == '#') << (i - 1);
  343.         for (i64 i = 1; i < parts.size() - 1; i++) {
  344.             auto ss = string_view{parts[i]}.substr(1, parts[i].size() - 2);
  345.             auto ints = splitInts(ss, ",");
  346.             i64 btn = 0;
  347.             for (auto v : ints)
  348.                 btn |= (1ll << v);
  349.             m.buttons.push_back(btn);
  350.         }
  351.         m.joltage = splitInts(string_view{parts.back()}.substr(1, parts.back().size() - 2), ",");
  352.         machines.push_back(move(m));
  353.     }
  354.     //print(machines);
  355.     print("Hello world");
  356.    
  357.     auto st = std::chrono::steady_clock::now();
  358.    
  359.     i64 totBest = 0;
  360.     i64 solTot = 0;
  361.     for (auto& machine : machines) {
  362.         //print(machine);
  363.         i64 best = INT_MAX;
  364.         iterateSets(machine.buttons.size(), [&](uint64_t b) {
  365.             unsigned val = 0;
  366.             iterateBits(b, [&](i64 ind) {
  367.                 val ^= machine.buttons[ind];
  368.                 return true;
  369.             });
  370.             if (val == machine.target) {
  371.                 best = popcount(b);
  372.                 return false;
  373.             }
  374.             return true;
  375.         });
  376.         //print(best);
  377.         totBest += best;
  378.  
  379.         Solver solver(machine);
  380.         auto sol = solver.solve();
  381.         print("Solver:", sol);
  382.         solTot += sol;
  383.     }
  384.  
  385.     print("part1:", totBest);
  386.     print("part2:", solTot);
  387.  
  388.     auto en = std::chrono::steady_clock::now();
  389.     auto diff = en - st;
  390.     print("Time:", (en - st) / 1.0ms, "ms");
  391.  
  392.  
  393.     return 0;
  394. }
Advertisement
Add Comment
Please, Sign In to add comment