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 成文
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号