题目
题解思路
题目不容易理解,用迪杰斯特拉好像不行,这里好像用源点出发容易理解点,用邻接表存图 (一开始佣金汇率输入反了 痛苦啊 )。
因为每一步的有的钱兑换出来的钱是不一样的可能亏可能赚,所以最好就以源点出发来进行对钱的兑换,从存入dis数组。
当一个dis的值被松弛的次数比 N还多时(在某个地方被多次松弛) 就存在正环 ! 就是不断加钱,刷钱的感觉。此时return即可!
SPFA
对每个点都可能多次入队 ,当可以松弛时,被松弛的点可能又入队,就可以多次松弛,这样就可以判断正环和负环!最短路也是行的,当所有路都是最短路时,没有点能再入队了,不断出队!
AC代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define INF 20000.0
struct bian
{
int z;
double h,y;
};
vector <bian> head[105];
bool vis [105];
int cnt[105];
double dis[105];
int main ()
{
int n,m,s;
bian tmm;
double sum;
cin>>n>>m>>s>>sum;
for (int i = 1 ;i <= m ;i++ )
{
int a,b;
double y,h;
cin>>a>>b>>h>>y;
tmm.z = b;
tmm.y = y;
tmm.h = h;
head[a].push_back(tmm);
cin>>h>>y;
tmm.z = a;
tmm.y = y;
tmm.h = h;
head[b].push_back(tmm);
}
/* for (int i = 1 ;i <= n ;i++ )
{
for (int k = 0 ;k<head[i].size() ; k++ )
{
printf("%d,%d,%f,%f ",i,head[i][k].z,head[i][k].y,head[i][k].h );
}
printf("\n");
} */
memset(vis, 0 ,sizeof(vis) );
memset(dis, 0 , sizeof(dis));
memset(cnt , 0 , sizeof(cnt) );
queue <int> q;
q.push(s);
dis[s] = sum ;
cnt[s]++;
double tmp;
vis[s] = 1 ;
while(!q.empty())
{
int t = q.front();
q.pop();
// printf("出队的点是:%d\n",t);
vis[t] = 0 ;
for (int i= 0 ; i<head[t].size() ; i++ )
{
tmp = (dis[t] - head[t][i].y)*head[t][i].h ;
if (dis[head[t][i].z] < tmp )
{
dis[head[t][i].z] = tmp ;
cnt[head[t][i].z]++ ;
if (cnt[head[t][i].z]>= n )
{
cout<<"YES\n";
return 0;
}
if ( vis[head[t][i].z] == 0 )
{
vis[head[t][i].z] = 1;
q.push(head[t][i].z);
}
}
}
/* for (int i = 1 ;i <= n ; i++ )
printf("dis[%d]=%f ",i,dis[i]);
printf("\n"); */
}
cout<<"NO\n";
return 0;
}