套汇问题
套汇是指利用货币对率的差异,把一个单位的某种货币转换为大于一个单位的同种货币的方法。例如,假定1美元可以买46.4印度卢比,1印度卢比可以买2.5日元,1日元可以买0.0091美元。通过货币兑换,一个商人可以从美元开始买入,得到46.4*2.5*0.0091=1.0556美元,因而获得5.56%的利润。
假定已知n种货币c1,c2,….,cn和有关兑换率的n*n的表R,1单位货币ci可以买入R[I,j]单位的货币cj.
a) 写入一个有效算法,以确定是否存在一个货币序列<ci1,ci2,ci3,ci4,…,cik>满足
R[i1,i2]* R[i2,i3]… R[ik-1,ik] *R[ik,i1] >1
并分析算法的运行时间。
b) 写出一个有效的算法来输出该序列(如果存在这样的序列的话),并分析算法的运行时间。
解:
基本解题思想:
通过FLOYD算法求出最大环。判断最大环的权值之积是否大于1,如果大于1说明可以实现套汇,如果不大于1 说明不能实现套汇。在求解最大环的同时记录下兑换过程中的步骤。
算法实现的数据结构:
int Path[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//用来记录套汇过程中要经过的路径
float value[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//用来记录经过讨回操作后得到的值
//借助图来实现该算法
typedef struct{
int vexs[MAX_VERTECX_NUM]; //顶点向量 每种货币对应一个顶点
float arc[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//邻接矩阵 存放兑换率信息
int vexnum,arcnum; //图中当前顶点数和弧数
}MGraph;
算法中的关键步骤:
for(k=1;k<=G->vexnum;k++)
{
for(i=1;i<=G->vexnum;i++)
{
for(j=1;j<=G->vexnum;j++)
{
if(value[i][k]*value[k][j]>value[i][j])//对FLOYD算法的修改,这里判断是否使兑换率增大,如果增大则记录下来
{
value[i][j]=value[i][k]*value[k][j];
Path[i][j]=Path[k][j];
}
}
}
}
在输出兑换序列时采用了递归算法:这个算法逆序输出了兑换序列。
void Procedure_print(int i,int j)
{
if(Path[i][j]==i)
{
printf("%d",i);
return;
}
else if(Path[i][j]==0)//输出结点i与结点j之间不存在通路
printf("NO path");
else
{
printf("%d ",Path[i][j]);
Procedure_print(i,Path[i][j]);//{递归,货币I至J中间顶点}
}
}
此算法的时间复杂度是:O(v^3)
算法实现代码:
#include<stdio.h>
#define MAX_VERTECX_NUM 20
#define INT_MIN 0
int n;
int Path[MAX_VERTECX_NUM][MAX_VERTECX_NUM];
float value[MAX_VERTECX_NUM][MAX_VERTECX_NUM];
typedef struct{
int vexs[MAX_VERTECX_NUM]; //顶点向量 可以存储每个顶点的信息
float arc[MAX_VERTECX_NUM][MAX_VERTECX_NUM];//邻接矩阵 主要存放关于边的信息
int vexnum,arcnum; //图中当前顶点数和弧数
}MGraph;
void CreateDG(MGraph *G){
int i,j,k;
float w;
scanf("%d%d",&(G->vexnum),&(G->arcnum));
printf("G->vexnum=%d,G->arcnum=%d\n",G->vexnum,G->arcnum);
for(i=1;i<=G->vexnum;i++)
{
G->vexs[i]=i;
}
for(i=1;i<=G->vexnum;i++)
{
for(j=1;j<=G->vexnum;j++)
{
G->arc[i][j]=INT_MIN;
}
}
for(k=1;k<=G->arcnum;k++)
{
scanf("%d%d%f",&i,&j,&w);
G->arc[i][j]=w;
}
}
void ShortestPath_FLOYD(MGraph *G)
{
int i,j,k;
for(i=1;i<=G->vexnum;i++)
{
for(j=1;j<=G->vexnum;j++)
{
if(i==j)
value[i][j]=1;
else
value[i][j]=G->arc[i][j];
if(G->arc[i][j]>INT_MIN)
Path[i][j]=i;
else
Path[i][j]=0;
}
}
for(k=1;k<=G->vexnum;k++)
{
for(i=1;i<=G->vexnum;i++)
{
for(j=1;j<=G->vexnum;j++)
{
if(value[i][k]*value[k][j]>value[i][j])
{
value[i][j]=value[i][k]*value[k][j];
Path[i][j]=Path[k][j];
}
}
}
}
}
void Procedure_print(int i,int j)
{
if(Path[i][j]==i)
{
printf("%d",i);
return;
}
else if(Path[i][j]==0)//输出结点i与结点j之间不存在通路
printf("NO path");
else
{
printf("%d ",Path[i][j]);
Procedure_print(i,Path[i][j]);
}
}
int main()
{
int i,j;
MGraph G;
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
CreateDG(&G);
ShortestPath_FLOYD(&G);
i=1;
if(value[i][i]>1)
{
printf("%f ",value[i][i]);
if(Path[i][i]!=0)
printf("%d%d ",i,i);
printf("兑换顺序的逆序输出:%d ",i);
Procedure_print(i,i);
printf("\n");
}
}