D:互联网反垄断

问题 D: 互联网反垄断

题目描述
互联网从来都不是法外之地。自12月2日起,互联网反垄断开启新的征程。先是蚂蚁集团被中国人民银行、中国银保监会、中国证监会、国家外汇管理局联合约谈。12月14日,阿里巴巴、阅文集团、丰巢网络3家公司,因收购其他公司未依法申报违法实施经营者集中,均被国家市场监管总局作出50万元的顶格行政处罚。

12月30日,国家市场监管总局再次出手,因“双十一”期间开展自营业务不正当价格行为对京东、天猫、唯品会三家企业分别处以50万元行政处罚。

市场经济中,充分公平的竞争才会淘汰劣币,垄断造成的后果必然是劣币驱逐良币。

正因为如此,我国允许各银行在合理区间内拥有金融服务手续费自主定价权。两个账户之间的转账是需要扣除手续费的,转账费率各家银行都不太一样。

已知有n个不同的银行账号,有m对账号是可以相互转账的。给你这m对账号转账需要扣除的手续费比例,那么请你基于这些数据,求出给定的两个账号间转账出多少元,对方账号能收到100元。

输入
第一行输入两个正整数n,m,分别表示总账号数和可以互相转账的账号的对数。(0<n<=2000)
以下m行每行输入三个正整数x,y,z,表示标号为x的账号和标号为y的账号之间互相转账需要扣除z%的手续费 (z<100)。
最后一行输入两个正整数A,B。
数据保证A与B之间可以直接或间接地转账。

输出
输出A转账使得B到账100元最少需要的总费用。精确到小数点后8位。

样例输入
3 3
1 2 1
2 3 2
1 3 3
1 3

样例输出
103.07153164

此题涉及到的知识点是Dijkstra算法,首先我来给大家分析一下样例输入后,样例输出是怎么得到的。
在这里插入图片描述
不难发现所求的费用是通过100除以前面(1-手续费率)乘积所得。

在这里插入图片描述
要使得费用x最低,即求得分母的最大值即可。在这里插入图片描述
本题边权值初始化为0%,即表明任何两个账户之间一开始都不能相互转账。读入一组x,y,z后,则将标号为x的账户和标号为y的账户之间的边存储为(1-z%)。运用Dijkstra算法的思想,求得分母的最大值。最后100/分母的最大值即可得到理想的输出结果。废话不多说,上代码!

#include <stdio.h>
#include <stdlib.h>

#define MAX 2001//账号个数即顶点数n(0<n<=2000)

int visit[MAX];
//定义一个全局数组visit[]用来标记顶点是否被访问过,1代表该顶点已经访问过,0代表该顶点未被访问过	。
//注:定义在main()函数外面的数组,默认初始化为0。
double dist[MAX];
/*
定义一个全局数组dist[]用来存储起始点start到顶点i之间的最大的(1-手续费率)的乘积,
注意到乘积不是整型,而是浮点型,
而最后输出要求精确到小数点后8位,为了安全,我们不妨定义成double类型。
*/

/*邻接矩阵存储图(顶点表和邻接矩阵)
  定义图的结构体
*/
typedef struct{
    
    
	int vex[MAX];//顶点表记录各个顶点信息,由题意显然顶点标号是从1到n。
	double edges[MAX][MAX];
	/*邻接矩阵记录各个顶点之间的关系,
	本题中顶点与顶点间的边表示两个账号之间的转账的(1-手续费率),因此是double类型。*/
	int vertexnum,edgenum; //定义顶点数和边数。
}MGraph,*gra;
/*typedef定义新的数据类型,给这个结构体类型取了一个新的别名MGraph,
MGraph类型即结构体类型,并定义了一个指向该结构体类型的指针,名为gra。
*/

//创建图操作
gra createGraph(int n,int m)//createGraph()函数来创建图,返回值类型是gra类型。
{
    
    
	gra G=(gra)malloc(sizeof(MGraph));
	//定义了一个gra类型的变量G,并用malloc()函数给其分配MGraph类型大小的空间。
	G->vertexnum=n;//图的顶点数为n。
	G->edgenum=m;//图的边数为m。
	
	int i,j;
	for(i=1;i<=G->vertexnum;i++)
	 G->vex[i]=i;  //给图的顶点进行初始化操作,本题中顶点编号为1-n。
	/*给图的边进行初始化操作,本题中边的权值一开始初始化为0,
	代表一开始任意两个账号间都不能进行转账操作。*/
	for(i=1;i<=G->vertexnum;i++)
	{
    
    
		for(j=1;j<=G->vertexnum;j++)
		 	G->edges[i][j]=G->edges[j][i]=0;//无向图
	}
	
	return G; //返回值类型是gra类型。
}

//求起始点start到目标点target(1-手续费率)乘积的最大值。
void dijkstra(gra G,int start,int target)
{
    
    
	int i,j;
	for(i=1;i<=G->vertexnum;i++)
	 dist[i]=G->edges[start][i];
	/* 一开始把起始点start到顶点i的(1-手续费率)乘积的最大值
	初始化为起始点start到顶点i的边的权值*/
	
	//求除起始点外的其他n-1个顶点到起始点(1-手续费率)乘积的最大值(Dijkstra算法思想)
	for(i=1;i<G->vertexnum;i++)
  {
    
    
	  double max=0;
	  int pos;
	  for(j=1;j<=G->vertexnum;j++)
	  {
    
    
	  	if((visit[j]==0)&&(dist[j]>max))
	  	{
    
    
	  		max=dist[j];
	  		pos=j;
		}
	  }	
	  	
	visit[pos]=1;
	  	
	if(pos==target)
	  break; //找到了起始点到目标点的(1-手续费率)乘积的最大值直接 break。
	
	for(j=1;j<=G->vertexnum;j++)
	{
    
    
		if((visit[j]==0)&&(dist[j]<dist[pos]*G->edges[pos][j]))
		 dist[j]=dist[pos]*G->edges[pos][j];
	}
  }
  
}

int main(int argc, char *argv[]) {
    
    
	int n,m,x,y,z,i,A,B;
	scanf("%d%d",&n,&m);//输入总账号数n和可以互相转账的账号对数m。
	
	gra G=createGraph(n,m);//创建图。
	for(i=1;i<=G->edgenum;i++)
	{
    
    
		scanf("%d%d%d",&x,&y,&z);
		//输入三个正整数x,y,z,表示标号为x的账号和标号为y的账号之间互相转账需要扣除z%的手续费。
		G->edges[x][y]=G->edges[y][x]=(double)(100-z)/100;//无向图。
		//边存储的权值为(1-手续费率),是double类型。
	}
	
	scanf("%d%d",&A,&B);//输入A,B。
	
	dijkstra(G,A,B);//求得A到B的最大(1-手续费率)的乘积。
	printf("%.8lf",100/dist[B]);
	/*100/(1-手续费率)的乘积的最大值即为费用的最小值,
	同时要求精确到小数点后八位。*/
	return 0;
}

本题考察的是Dijkstra算法的思想,不是硬搬Dijkstra算法的代码,因此不能生搬硬套,要灵活运用。
难度系数为中等。
这次的分享就先到这,有不明白的小伙伴可以在评论区给我留言。感谢大家捧场!

扫描二维码关注公众号,回复: 12546542 查看本文章

猜你喜欢

转载自blog.csdn.net/qq_46139801/article/details/113454703