题目链接:
https://codeforces.com/problemset/problem/1266/D
题目大意:
有 \(n\) 个人,有 \(m\) 条债务关系,定义 \(d(a, b)\) 表示 \(a\) 欠 \(b\) 块钱,如果 \(c\) = 0,那么就相当于没有债务关系,现在想简化每个人之间的债务关系。
有以下两种简化方案,
1、当 d(a, b) > 0,d(c, d) > 0 且 a != c 或者 b != d 时,我们可以让 d(a, b),d(c, d) 都减去 z,d(c, b),d(a, d)都加上 z,0 < z <= min(d(a, b),d(c, d))。
2、当 d(a, a) > 0 时,我们可以设 d(a, a) = 0。
问最后能简化成什么怎样的关系,并将简化后的所有关系总数先输出,再依次输出每条关系。
思路:
在第一条简化方案中我们可以令 \(b = c\),那么我们可以分三种情况看。
当 d(a, b) > d(c, d) 时,d(a, b),d(c, d) 最后就变成 d(a, b),d(c, b) (即 d(b, b)), d(a, d)三个,再根据第二种方案,答案就变成了 d(a, b),d(a, d)。
当 d(a, b) < d(c, d) 时,通过第二个简化方案,d(a, b),d(c, d) 最后变成 d(c, d),d(a, d)。
当二者相等时,化简后结果就是 d(a, d)。
综上,所有的化简都会使某个人的关系变成只是借给别人钱或者只是向别人借钱,那么最后的答案就是债主与负债人进行一个匹配就行。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 1e5 + 10;
struct node {
LL k, index;
}a[N], b[N];
struct nd{
LL s, e, to;
}ans[N];
LL n, m, u, v, d, num[N], cnt1, cnt2, sum;
void solve(){
cin >> n >> m;
for (int i = 1; i <= m; i++){
scanf("%lld%lld%lld", &u, &v, &d);
num[u] += d; //向人借钱
num[v] -= d; //借钱给别人
}
for (int i = 1; i <= n; i++){
if (num[i] > 0) a[++cnt1].k = num[i], a[cnt1].index = i; //负债人
else if (num[i] < 0) b[++cnt2].k = -num[i], b[cnt2].index = i; //债主
}
int p = 1, q = 1;
while (p <= cnt1){
LL minn = min(a[p].k, b[q].k);
a[p].k -= minn;
b[q].k -= minn;
ans[++sum].s = a[p].index;
ans[sum].e = b[q].index;
ans[sum].to = minn;
if (a[p].k == 0) p++; //借到了想借的钱
if (b[q].k == 0) q++; //借出了要借的钱
}
cout << sum << "\n";
for (int i = 1; i <= sum; i++)
printf("%lld %lld %lld\n", ans[i].s, ans[i].e, ans[i].to);
}
int main(){
solve();
return 0;
}