Codeforces Global Round 6 D.Decreasing Debts(图,思维)超详细题解 和样例答案一模一样的写法

写出来之后和样例居然一摸一样,,我也是惊了
思路:
先说结论,最后剩下来的所有债务就是所有点的入度之和或者出度之和。
为什么呢?
题目给了两种化简方式:
在这里插入图片描述
即:
1.d(a,b)>0和d(c,d)>0的情况下,可以让d(a,b)和d(c,d)共同减去z,再给d(a,d)和d(c,b)共同加上z;
2.如果d(a,a)这样出入两点都是自己的情况下,则这个可以直接消去

先从样例给的这种情况看:
在这里插入图片描述
如果有d(1,2) = 10,d(2,3) = 5;
则设a=1, b=c=2, d=3;
第一步
d(1,2)=d(1,2)-5=5-5=0;
d(2,3)=d(2,3)-5=10-5=5;
d(1,3)=d(1,3)+5=0+5=5;
d(2,2)=d(2,2)+5=0+5=5;
第二步
消掉d(2,2)则只剩下d(2,3)=d(2,3)-5=10-5=5和d(1,3)=d(1,3)+5=0+5=5;

我们总结规律,
在这里插入图片描述
像这样的情况,1到2到3到4到5之间这么多条x的债务,是不是都可以直接化成1到5之间x的债务,其他的都可以消掉?
先化成这样的图,然后再消掉自己欠自己的钱
在这里插入图片描述
感性地理解,就是如果1号还给2号x块钱,2号就会把这x块钱拆东墙补西墙地给3号,3号也用这笔钱还给4号,依次类推,即化简为1欠5号x块钱。
总结规律,像中间这种入度=出度的点其实都是不欠债务的。(用第二种方法消去)
所以只需要处理入度!=出度的点。

程序实现:
1.用一个degr[MAXN]数组存入度-出度,为0则代表入度出度相等。
2.读入数据d(a,b)=x;存入degr数组:
degr[a]-=x;degr[b]+=x;
3.遍历所有点的degr[],用vector< int >dout保存出度>入度的点,用vector< int >din保存出度<入度的点。
4.用pxin作din的指针,pxout作dout的指针
5.因为图所有点的入度之和=出度之和,所以dout的degr之和与din的degr之和正好是相反数。

代码实现:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;

#define rep(i,a,b) for(int i=(a);i<=(b);i++)

const int MAXN = 1e5+10;

long long degr[MAXN];//记录入度-出度 
int n;

struct debt{//用结构体存和vector存储结果 
	int from,to;
	long long flow;
	debt(int f,int t,long long fl):from(f),to(t),flow(fl){}
};

int main(){
	vector<int>dout;
	vector<int>din;
	vector<debt>Res;
	int n,m;
	scanf("%d%d",&n,&m);
	memset(degr,0,sizeof(degr));
	while(m--){
		int a,b,deb;
		scanf("%d%d%d",&a,&b,&deb);//读入数据 
		degr[a]-=deb;
		degr[b]+=deb;
	}
	long long res = 0;
	rep(i,1,n){
		if(degr[i]>0)din.push_back(i);
		else if(degr[i]<0)dout.push_back(i);
	}
	int doutsiz = dout.size();//存出度>入度的点编号 
	int dinsiz = din.size();//存入度>出度的点编号 
	int pxout = 0;
	int pxin = 0;
	while(pxout<doutsiz&&pxin<dinsiz){//一直处理到所有点都被处理完毕 
		long long flow = min(-degr[dout[pxout]],degr[din[pxin]]);
		degr[dout[pxout]]+=flow;
		degr[din[pxin]]-=flow;
		Res.push_back(debt(dout[pxout],din[pxin],flow));//把这个结果存入Res,方便一会儿计数输出 
		if(degr[dout[pxout]]==0) pxout++;//如果当前pxout指向的degr已经被消耗完了,则换下一个 
		if(degr[din[pxin]]==0) pxin++;//如果当前pxin指向的degr已经被消耗完了,则换下一个 
	}
	int ressiz = Res.size();
	cout<<ressiz<<endl;
	rep(i,0,ressiz-1){
		cout<<Res[i].from<<' '<<Res[i].to<<' '<<Res[i].flow<<endl;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值