1. 程式人生 > 其它 >CF1266D - Decreasing Debts(構造性演算法 + 資料結構 + 貪心 + 數學規律 / 銅牌級)

CF1266D - Decreasing Debts(構造性演算法 + 資料結構 + 貪心 + 數學規律 / 銅牌級)

【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 成文