CF1266D - Decreasing Debts(構造性演算法 + 資料結構 + 貪心 + 數學規律 / 銅牌級)
阿新 • • 發佈:2022-01-17
【2022.01.09】隊內賽第四題,【2022.01.17】補題
1266D - Decreasing Debts(源地址自⇔CF1266D)
目錄tag
⇔構造性演算法、⇔資料結構、⇔貪心、⇔數學規律、⇔銅牌級(*2000)
題意
場上有 \(n\) 個人與 \(m\) 條欠債關係( \(u_i,v_i,w_i\) 代表 \(u\) 借給了 \(v\) \(w\) 元),由於被借錢的人也會再向其他人借錢,就會形成一個異常複雜的借錢網路。我們都知道,債務關係是可以轉移的,現在,請你簡化這個網路,使得簡化後的網路涉及的金額達到最小。輸出任意一個簡化後的網路。
思路
自己思路(錯誤)
剛開始理所當然的想到圖論的解法,即簡化路徑。首先想到並查集,但是由於無法確定一個絕對的祖先節點(不欠錢的人),所以無法處理,後來又轉而向到了 \(\tt{Tarjan}\) 的縮點,但實際上關係也不大(會建立新的欠債關係)。
金老師思路(貪心)
我們可以準確的求出某個人到底是欠了錢還是借出了錢,以及金額是多少——那麼,重新、任意地連結這些人,的出來的新網路必然是最優的(用欠債人的錢還借出了錢的人)。
邦神補充:就好像打麻將時,輸了錢的人直接將錢放桌上,而不必直接算清,贏了錢的人去任意的桌上拿走自己贏了的金額即可。
AC程式碼(貪心,元組)
備註:程式碼使用的元組的某些技巧涉及到C++17的新性質,同樣可以使用結構體解,此處不再另寫一份。
點選檢視程式碼
//==================== LL num, a[MAX]; bool Ans; stack<pair<LL, LL> > los, win; vector<tuple<LL, LL, LL> > ans; LL u, v, w, n, m; //==================== void Solve() { cin >> n >> m; FOR(i, 1, m) { cin >> u >> v >> w; a[u] -= w; a[v] += w; } FOR(i, 1, n) { if(a[i] > 0) los.push({i, a[i]}); else if(a[i] < 0) win.push({i, -a[i]}); } while(!win.empty()) { pair<LL, LL> x = win.top(); win.pop(); pair<LL, LL> y = los.top(); los.pop(); if(x.second > y.second) { ans.push_back({x.fi, y.fi, y.se}); win.push({x.fi, x.se - y.se}); }else if(x.se < y.se) { ans.push_back({x.fi, y.fi, x.se}); los.push({y.fi, y.se - x.se}); }else { ans.push_back({x.fi, y.fi, x.se}); } } cout << ans.size() << endl; for(auto [u, v, w] : ans) cout << u << " " << v << " " << w << endl; }
錯誤次數
無
文 / WIDA
2022.01.17 成文
首發於WIDA個人部落格,僅供學習討論
更新日記:
2022.01.17 成文