CF1266D - Decreasing Debts(构造性算法 + 数据结构 + 贪心 + 数学规律 / 铁牌级)

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.18 修改标签
2022.01.17 成文


posted @ 2022-01-17 17:02  hh2048  阅读(34)  评论(0编辑  收藏  举报