HDU 1217Arbitrage(Floyd,SPFA两种解法解析)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1217

题目大意:这道我们需要判断能否套利,套利就是能否通过一系列的不同货币间的转换获利,因此我们可以将这道题转化为求最大路的题目,需要注意的是这道题当乘积小于1时,实际上是相当于加法里面的负权的,所以我们这道题不能使用dijkstra算法。这道题站点(也就是货币名称)是以字符串形式给出的,所以我们首先需要做的就是把这些字符串转换成数字编号,然后这道题就变成了简单的Floyd或SPFA问题了。

Floyd代码:

#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=35;
int n,m;
double mp[maxn][maxn];
void floyd(){//简单Floyd
	int i,j,k;
	for(i=1;i<=n;i++){
		for(j=1;j<=n;j++){
			for(k=1;k<=n;k++){
				if(mp[j][i]!=-1&&mp[i][k]!=-1)//这个判断不要也能过,不过我还是喜欢加上
				if(mp[j][i]*mp[i][k]>mp[j][k])//求最大路
				mp[j][k]=mp[j][i]*mp[i][k];
			}
		}
	}
}
int main(){
	int i,j,k,Case=1;
	map<string,int>name;
	while(cin>>n&&n){
		for(i=1;i<=n;i++){
			for(j=1;j<=n;j++){
				if(i==j)
				mp[i][i]=1;//在没有给出数据前,任何站点(货币)都只能到达自己
				else
				mp[i][j]=-1;
			}
		}
		for(i=1;i<=n;i++){
			string s;
			cin>>s;
			name[s]=i;//字符串转换成数字编号
		}cin>>m;
		string s1,s2;double x;
		for(i=1;i<=m;i++){
			cin>>s1>>x>>s2;
			mp[name[s1]][name[s2]]=x;//读入边
		}
		floyd();
		for(i=1;i<=n;i++){
			if(mp[i][i]>1){
			cout<<"Case "<<Case<<": "<<"Yes"<<endl;
			goto a;
			}
		}
		cout<<"Case "<<Case<<": "<<"No"<<endl;
		a:Case++;
	}
}

 

SPFA代码:

#include<iostream>
#include<string>
#include<queue>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int maxn=35;
struct edge{
	int v,next;//v表示顶点连接的一个点,next表示顶点所连接的下一条边
	double val;//val表示边权
}e[50*maxn];//50*maxn表示边的最大数量,这个边的数量定小了的话用G++提交为tle,用c++交会WA
int n,m,vis[maxn],head[maxn];//head表示要加入的边的编号
double dis[maxn];
bool spfa(int t){
	int i,j,k;
	for(i=1;i<=n;i++){
		if(i==t)
		dis[i]=1.0;//SPFA算法是通过边来更新值,所以这里一开始设置dis[t]=1
		else
		dis[i]=0;
	}
	queue<int>q;
	q.push(t);//入队
	vis[t]=1;//标记入队
	while(!q.empty()){
		int u=q.front();
		q.pop();//出队首
		vis[u]=0;//标记u点已不在队列
		k=head[u];//k为u点所连接的边的编号
		while(k!=-1){
			if(dis[u]*e[k].val>dis[e[k].v]){
				dis[e[k].v]=dis[u]*e[k].val;
				if(dis[t]>1.0)
				return true;
				if(!vis[e[k].v]){//若该点不在队列中则入队
					vis[e[k].v]=1;
					q.push(e[k].v);
				}
			}
			k=e[k].next;//更新k为u点所连接的下一条边
		}
	}
	return false;
} 
int main(){
	int i,j,k,Case=1;
	map<string,int>name;
	while((scanf("%d",&n)==1)&&n){
		int flag=0;
		memset(vis,0,sizeof(vis));
		memset(head,-1,sizeof(head));
		for(i=1;i<=n;i++){
			char s[1000];
			scanf("%s",s);
			name[s]=i;//同上,转换为数字编号
		}
		cin>>m;
		double x;
		for(i=1;i<=m;i++){
			char s1[1000],s2[1000];
			scanf("%s%lf%s",s1,&x,s2);
                        //这三行代码是建立邻接表的关键
			e[i].val=x;//边权读入
			e[i].v=name[s2];//边连接的另个一点读入
			e[i].next=head[name[s1]];//下一条边读入
			head[name[s1]]=i;//更新head的编号,置于栈顶
		}
		for(i=1;i<=n;i++){
			if(spfa(i)){
				flag=1;
				break;
			}
		}
		printf("Case %d: %s\n",Case,flag==1?"Yes":"No");
		Case++;
	}
}

 

 

 

 

 

posted @ 2018-06-17 16:14  Zookkk  阅读(157)  评论(0编辑  收藏  举报