poj2240(关于spfa判断正环以及做此题时遇到的问题,有易错点分析啊啊啊啊)

题目
poj2240
Arbitrage
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 31364 Accepted: 13014
Description

Arbitrage is the use of discrepancies in currency exchange rates to transform one unit of a currency into more than one unit of the same currency. For example, suppose that 1 US Dollar buys 0.5 British pound, 1 British pound buys 10.0 French francs, and 1 French franc buys 0.21 US dollar. Then, by converting currencies, a clever trader can start with 1 US dollar and buy 0.5 * 10.0 * 0.21 = 1.05 US dollars, making a profit of 5 percent.

Your job is to write a program that takes a list of currency exchange rates as input and then determines whether arbitrage is possible or not.
Input

The input will contain one or more test cases. Om the first line of each test case there is an integer n (1<=n<=30), representing the number of different currencies. The next n lines each contain the name of one currency. Within a name no spaces will appear. The next line contains one integer m, representing the length of the table to follow. The last m lines each contain the name ci of a source currency, a real number rij which represents the exchange rate from ci to cj and a name cj of the destination currency. Exchanges which do not appear in the table are impossible.
Test cases are separated from each other by a blank line. Input is terminated by a value of zero (0) for n.
Output

For each test case, print one line telling whether arbitrage is possible or not in the format “Case case: Yes” respectively “Case case: No”.
Sample Input

3
USDollar
BritishPound
FrenchFranc
3
USDollar 0.5 BritishPound
BritishPound 10.0 FrenchFranc
FrenchFranc 0.21 USDollar

3
USDollar
BritishPound
FrenchFranc
6
USDollar 0.5 BritishPound
USDollar 4.9 FrenchFranc
BritishPound 10.0 FrenchFranc
BritishPound 1.99 USDollar
FrenchFranc 0.09 BritishPound
FrenchFranc 0.19 USDollar

0
Sample Output

Case 1: Yes
Case 2: No

大意翻译一下:以第一个样例
第一个3:表示一共有三种货币
接下来三行是货币的名称
第二个3:表示有几种货币交换
接下来是交换方式如
USDollar 0.5 BritishPound表示 1USDollar换0.5BritishPound
也就是说这个图是有向图
最后让你输出yes表示在货币交换的时候是否换来换去让你的钱变多了,
你本来有1人民币,换成美元,又换成英镑再换成人名币变成5人民币了,你就赚了是吧
所以必须得成环啊,而且是正环,也就是用spfa判正环了
易错点分析

  • 图是有向图

  • 存在FrenchFranc 10 FrenchFranc 这种数据,你说说这题是人出的吗!!!!!
    对于这种数据你可以在一开始读取的时候判断,也可以在spfa中判断
    图可能不是联通的,如下图,分成两部分,上一部分没有正环,下一个有


    我当时wa的代码,感觉特别对,测试样例以及自己想的样例都没·问题,后来发现了,看我的注释吧

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
map<string,int>loc; 
int n,m;
int book[1000];
double book1[1000];
double dis[1000];
struct tu{
	int y;
	double c;
};
vector<tu> v[1000];
queue<int> q;
void init(int p)
{
	for(int i=0;i<=p;i++)
	{
		v[i].clear();
	}
}
int main()
{
	char chs[50],che[50];
	int t,i,j,zhou,flag=0,total=0;
	double a1;
	while(scanf("%d",&n)&&n)
	{
		total++;
		init(n);
		t=1;
		flag=0;
		loc.clear();
		q= queue<int>();   
		for(i=0;i<n;i++)
		{
			scanf("%s",chs);
			loc[chs]=t++;
		}
		scanf("%d",&m);
		for(i=0;i<m;i++)//建图
		{
			scanf("%s%lf%s",chs,&a1,che);
			/
			struct tu tmp;
			tmp.c=a1;
			tmp.y=loc[che];
			v[loc[chs]].push_back(tmp);
		}
	   memset(book,0,sizeof(book));
		q.push(loc[che]);//我放的是最后录进去的一种钱币,从他开始spfa,然后开始找环,问题是这个图可
		book[loc[che]]=1;//是不联通的,可能正环在图的另一部分,所以错了
	     memset(book1,0,sizeof(book1));
		memset(dis,0,sizeof(dis));
		dis[loc[che]]=1;
		book1[loc[che]]=1;//这个数组是用来存第一次遍历后手中的钱的数量,数组的下表表示的是钱的种类,哪                  //种货币
		
		while(!q.empty())
		{
		   zhou=q.front();
		   
		  for(i=0;i<v[zhou].size();i++)
		  {
			if(dis[v[zhou][i].y]<dis[zhou]*v[zhou][i].c&&book[v[zhou][i].y]==0)
			{
				
			//	printf("dis[zhou]*v[zhou][i].c  %lf\n ",dis[zhou]*v[zhou][i].c);
			//	printf("dis[v[zhou][i].y]  %lf\n ",dis[v[zhou][i].y]);
				dis[v[zhou][i].y]=dis[zhou]*v[zhou][i].c;
				if(dis[v[zhou][i].y]>book1[v[zhou][i].y]&&book1[v[zhou][i].y]!=0)
				{
					flag=1;
					break;
				}
				book1[v[zhou][i].y]=dis[zhou]*v[zhou][i].c;
				book[v[zhou][i].y]=1;
				q.push(v[zhou][i].y);
			}
		  }
		  if(flag==1)
		  {
		  	break;
		  }
		  q.pop();
		  book[zhou]=0;
		}
		if(flag==1)
		{
			printf("Case %d: Yes\n",total);
		}
		else{
			printf("Case %d: No\n",total);
		}
	}
	return 0;
}

不知道你们知道我错在哪里了么
给你组样例跑跑吧
3
USDollar
BritishPound
FrenchFranc
3
USDollar 10 BritishPound
BritishPound 10.0 USDollar
FrenchFranc 0.6 FrenchFranc
可以发现前两个样例
USDollar 10 BritishPound
BritishPound 10.0 USDollar是一部分,且是正环
FrenchFranc 0.6 FrenchFranc是图的另一部分,是一个负环,而我spfa就是从这读进去的,所以输出no

看正确代码,ac

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
map<string,int>loc; 
int n,m;
int book[1000];
double book1[1000];
double dis[1000];
struct tu{
	int y;
	double c;
};
int zhou;
vector<tu> v[1000];

void init(int p)
{
	for(int i=0;i<=p;i++)
	{
		v[i].clear();
	}
}
int spfa(int u)//代表要找哪个点
{
	int i,j;
		queue<int>q;
	  memset(book,0,sizeof(book));
		q.push(u);
		book[u]=1;
		memset(dis,0,sizeof(dis));
		dis[u]=1;//一开始u货币有1
		while(!q.empty())
		{
		   zhou=q.front();
		   q.pop();
		   book[zhou]=0;
		  for(i=0;i<v[zhou].size();i++)
		  {
			if(dis[v[zhou][i].y]<dis[zhou]*v[zhou][i].c)
			{
				
			//	printf("dis[zhou]*v[zhou][i].c  %lf\n ",dis[zhou]*v[zhou][i].c);
			//	printf("dis[v[zhou][i].y]  %lf\n ",dis[v[zhou][i].y]);
				dis[v[zhou][i].y]=dis[zhou]*v[zhou][i].c;
				if(dis[u]>1) //如果走来走去 进来的是u货币,只有1,后来变得大于1了,说明存在正环
				{
					
				
					return 1;
				}
				if(book[v[zhou][i].y]==0)
				{
				
				book[v[zhou][i].y]=1;
				q.push(v[zhou][i].y);}
			}
		  }
	
	    } 
	    return 0;
	
} 
int main()
{
	char chs[50],che[50];
	int t,i,flag,j,total=0;
	double a1;
	while(scanf("%d",&n)&&n)
	{
		total++;
		init(n);
		t=1;
		flag=0;
		loc.clear();
		
		for(i=0;i<n;i++)
		{
			scanf("%s",chs);
			loc[chs]=t++;//涉及到了map的用法,就是钱的种类转化为序号
		}
		scanf("%d",&m);
		for(i=0;i<m;i++)
		{
			scanf("%s%lf%s",chs,&a1,che);
			struct tu tmp;
			tmp.c=a1;
			tmp.y=loc[che];
			v[loc[chs]].push_back(tmp);//vector链式前向星存图,不懂得百度吧
		}
	for(i=1;i<=n;i++)
	{
		if(spfa(i))//调用函数
		{
			flag=1;
			break;
		}
	}
		if(flag==1)
		{
			printf("Case %d: Yes\n",total);
		}
		else{
			printf("Case %d: No\n",total);
		}
	}
	return 0;
}

把spfa变成函数,从一到n都调用一下,相当于把图的每一部分都走过了,看是否有正环

还有的要注意

如何判断有正环,有两种方法,先说我的这种,按着题意来的,就是换来换去这种钱变多了,一开始手里有1元u这种货币,后来大于1元了

	if(dis[u]>1) 
	{
		return 1;
	}

第二种,判断一个点放进去了几次,如果大于n次就说明有正环


if(up[p.y]>=n) return 1;

在一个,最后出的错误

if(dis[v[zhou][i].y]<dis[zhou]*v[zhou][i].c&&book[v[zhou][i].y]==0)//删去&&book[v[zhou][i].y]==0就对了
			{
				
			//	printf("dis[zhou]*v[zhou][i].c  %lf\n ",dis[zhou]*v[zhou][i].c);
			//	printf("dis[v[zhou][i].y]  %lf\n ",dis[v[zhou][i].y]);
				dis[v[zhou][i].y]=dis[zhou]*v[zhou][i].c;
				if(dis[u]>1) 
				{
					
				
					return 1;
				}
				book1[v[zhou][i].y]=dis[zhou]*v[zhou][i].c;
				book[v[zhou][i].y]=1;
				q.push(v[zhou][i].y);
			}
		  }
	

之所以删去&&book[v[zhou][i].y]==0就对了是因为这一句是为了判断队列中有没有这个点,
有就别放进去了,没有就应该push进去
希望对大家有些帮助

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值