POJ - 1860 (bellman最短路正权圈与负权圈判断
题意 有多种汇币,汇币之间可以交换,这需要手续费,当你用100A币交换B币时,A到B的汇率是29.75,手续费是0.39,那么你可以得到(100 - 0.39) * 29.75 = 2963.3975 B币。问s币的金额经过交换最终得到的s币金额数能否增加 货币的交换是可以重复多次的,所以我们需要找出是否存在正权回路,且最后得到的s金额是增加的 怎么找正权回路呢?(正权回路:在这一回路上,顶点的权值能不断增加即能一直进行松弛) Sample Input 3 2 1 20.0 1 2 1.00 1.00 1.00 1.00 2 3 1.10 1.00 1.10 1.00
即求是否存在一条可以无限松弛的正权回路;
bellman-ford 算法:每次循环判断所有边是否可以松弛,最糟糕情况每次只能对两个点之间边进行松弛,此时若无形成负权圈(求最短路经的情况),最多外层循环n-1(n为点的数量)次就可求出最短路,若此时仍未求出 ,则存在负权回路
//Bellman-Ford算法核心语句 for(k=1;k<=n-1;k++) //外循环循环n-1次,n为顶点个数 for(i=1;i<=m;i++)//内循环循环m次,m为边的个数,即枚举每一条边 if(dis[v[i]]>dis[u[i]]+w[i])//尝试对每一条边进行松弛,与Dijkstra算法相同 dis[v[i]]=dis[u[i]]+w[i]; //检测负权回路 flag=0; for(i=1;i<=m;i++) if(dis[v[i]]>dis[u[i]]+w[i]) flag=1; if(flag==1) printf("此图有负权回路");
本题是求正权回路,方法一样,注意double比较的精度问题
#include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <string> #include <vector> #include <queue> #define ll long long #define MAXN 1000000+50 #define mst( x,a ) memset ( x ,a,sizeof ( x ) ) using namespace std; const int inf = 0x3f3f3f3f ; struct edge{ int to; double w,mny; }; int ans_d[1005] ,vis[1005] ; double ans[1005]; int main( ){ mst ( ans,0); mst (vis ,0); int n,m ,st; double mny; vector <edge> es[1005]; scanf( "%d%d" ,&n ,&m); scanf( "%d%lf" ,&st ,&mny ); for( int i = 0; i<m ;i++){ int a,b; double w,mny; edge e; scanf( "%d%d%lf%lf",&a ,&b ,&w ,&mny); e.to = b; e.w=w; e.mny=mny; es[a].push_back ( e ); scanf("%lf%lf" ,&w ,&mny ); e.to=a; e.w=w; e.mny=mny; es[b].push_back( e ); } ans [st]=mny; for( int k=0 ;k<n ;k++ ){ for( int i=1 ; i<=n ; i++){ int sz=es[i].size( ); for( int j=0 ; j<sz ; j++){ int to=es[i][j].to; double w1=es[i][j].w,mny1=es[i][j].mny; // cout<<i<<"->"<<to<<' '<<(ans[i]-mny1)*w1<<endl; double cmp=(ans[i]-mny1)*w1; if( ans[to] < cmp ){ // cout <<ans[to]<<" "; ans[to] = cmp ; //cout <<ans[to] <<endl; if(k == n-1){printf("YES"); return 0;} } } } } printf("NO"); return 0; }