【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛

A. 债权(debt)

文件IO:debt 1000ms 256MiB

文章目录

题目描述

天色已晚,小 Y 决定在一处村庄过夜。这座村庄中有 n n n 个人,他们之间存在 m m m 个欠债关系。例如 A A A 欠 B B B 五元钱,而每还款 1 1 1 元钱就会给村子带来 0.1 0.1 0.1 元的成本。村长很苦恼如何能够通过债权转让的方式让成本最小化,于是求助于小 Y。现在小 Y 已经帮助村长完成了债权的转让,只需要你计算出最小化后的成本即可。

债权转让 :若 A A A 欠 B B B 五元钱, B B B 欠 C C C 五元钱,可以通过债权转让变为 A A A 欠 C C C 五元钱。

输入格式

输入文件的第一行包含两个正整数 n , m n,m n,m,以空格隔开,表示一共有 n n n 个人和 m m m 个欠债关系。

接下来的 m m m 行每行包含三个正整数 x , y , z x,y,z x,y,z,以空格隔开,表示 x x x 欠 y y y 的钱数为 z z z。

输出格式

输出一个保留一位的小数,表示最小化后的成本。

样例 1

样例输入 1

text 复制代码
5 3
1 2 5
2 3 3
4 5 4

样例输出 1

text 复制代码
0.9

样例说明 1

1 1 1 欠 2 2 2 五元钱、 2 2 2 欠 3 3 3 三元钱可以转变为 1 1 1 欠 2 2 2 两元钱、 1 1 1 欠 3 3 3 三元钱,然后可以得到一共需要还款 9 9 9 元钱,成本为 0.9 0.9 0.9 元钱。

样例 2

样例输入 2

text 复制代码
2 2 
1 2 5
2 1 5

样例输出 2

text 复制代码
0.0

样例说明 2

1 1 1 和 2 2 2 债务相抵后互不相欠,成本为 0.0 0.0 0.0 元。

数据范围

  • 子任务 1 1 1( 30 30 30 分): n ≤ 10 n\le 10 n≤10, m ≤ 20 m\le 20 m≤20。
  • 子任务 2 2 2( 30 30 30 分): n ≤ 100 n\le 100 n≤100, m ≤ 10000 m\le 10000 m≤10000。
  • 子任务 3 3 3( 40 40 40 分):无特殊限制。

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 100000 1\le n\le 100000 1≤n≤100000, 1 ≤ m ≤ 200000 1\le m\le 200000 1≤m≤200000, 1 ≤ x , y ≤ n 1\le x,y\le n 1≤x,y≤n, 1 ≤ z ≤ 10 9 1\le z\le 10^9 1≤z≤109。

题解

考虑图论建模,对于A欠B x元钱,构建一条(A,B,x)的边

题目允许的"债权转让"操作,可以理解为对于A->B->C "抄近路"变成A->C

考虑边权的变化,设t=min(gab,gbc),则有gab-=t,gbc-=t,gac+=t(前提a!=c)

有一个关键观察:不管如何转移,每个点的"净欠款"是不变的!!!(例如点A:A->B减少t,A->C增加t,所以不变)

di=outi-ini,若di>0说明它净欠别人di元,若di<0说明别人欠它(它净被欠)di元

因为有人欠钱,就一定有相应的人被欠钱,所以 ∑ d i = 0 \sum{di}=0 ∑di=0

可以理解为村里有一个"银行",欠钱的人交di元给银行,被欠钱的人从银行得到di元

银行只是中转,最后的总还款额就是所有净欠钱人的欠款总和: ∑ d i > 0 d i \sum_{di>0}di ∑di>0di

又因为正负净额相等,所以 ∑ d i > 0 d i = 1 / 2 ∗ ∑ ∣ d i ∣ \sum_{di>0}di = 1/2 * \sum{|di|} ∑di>0di=1/2∗∑∣di∣(因为每个欠钱会被算2次,它的起点、终点各算一次)

这就是最小还款总额

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+7;

int n,m; 
ll in[N],out[N];

void solve()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y,z; cin>>x>>y>>z;
		in[y]+=z,out[x]+=z;
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		ans+=llabs(out[i]-in[i]);
	}
	ans/=2;
	cout<<ans/10<<'.'<<ans%10;
}

signed main()
{
	freopen("debt.in","r",stdin);
	freopen("debt.out","w",stdout);
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	solve();
	return 0;
}
相关推荐
xiaoye-duck21 分钟前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型
思麟呀43 分钟前
背包DP(01背包+完全背包)
c++·算法
笑容掩饰我的失落2 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
旖旎夜光2 小时前
【C++ 基础入门】C++11 新特性(中):右值引用、移动语义与完美转发
开发语言·c++·笔记·学习
ShineWinsu2 小时前
对于Qt:QTabWidget常见属性的解析
开发语言·数据库·c++·qt·浏览器·qtabwidget·标签页
Pniubi3 小时前
力扣24. 两两交换链表中的节点(递归)
算法·leetcode·链表
꧁细听勿语情꧂3 小时前
直接选择排序,堆排序,冒泡排序,递归快速排序、hoare版本、挖坑法、lomuto前后指针,非递归快速排序
c语言·开发语言·数据结构·算法
俊俊几年的4 小时前
动态规划(Dynamic Programming)详解:从入门到 LeetCode Hot 100
linux·c++·算法·leetcode·嵌入式·动态规划·c
星恒随风13 小时前
MySQL 从零入门(五):MySQL 内置函数详解——日期、字符串、数学函数与实战案例
linux·数据库·笔记·sql·mysql