题目链接: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++;
}
}