[kuangbin带你飞]专题四 最短路练习-E

166 篇文章 0 订阅
32 篇文章 0 订阅

http://poj.org/problem?id=1860
Description

Several currency exchange points are working in our city. Let us suppose that each point specializes in two particular currencies and performs exchange operations only with these currencies. There can be several points specializing in the same pair of currencies. Each point has its own exchange rates, exchange rate of A to B is the quantity of B you get for 1A. Also each exchange point has some commission, the sum you have to pay for your exchange operation. Commission is always collected in source currency.
For example, if you want to exchange 100 US Dollars into Russian Rubles at the exchange point, where the exchange rate is 29.75, and the commission is 0.39 you will get (100 - 0.39) * 29.75 = 2963.3975RUR.
You surely know that there are N different currencies you can deal with in our city. Let us assign unique integer number from 1 to N to each currency. Then each exchange point can be described with 6 numbers: integer A and B - numbers of currencies it exchanges, and real RAB, CAB, RBA and CBA - exchange rates and commissions when exchanging A to B and B to A respectively.
Nick has some money in currency S and wonders if he can somehow, after some exchange operations, increase his capital. Of course, he wants to have his money in currency S in the end. Help him to answer this difficult question. Nick must always have non-negative sum of money while making his operations.

题意:

货币之间可以转换,有税有率,给N种货币可以相互转换,问能不能换来换去之后可以使最开始某种货币的钱变多(最后还是这种货币)

tip:

第一遍用了dijstra。。然后t了,后来发现dij之所以不能处理负环其实说到底是出不来,不断的更新,不断的入队列,但是bellman不一样,他规定了n-1层循环,只让你更新这么多次,如果可以更新n次则一定出现负环

因为最短路径的边数最多为 N-1,而 由数学归纳法知:当外循环为第 I 次时,我们算出了长度为 I 的最短路,而经过
第 I+1 次的循环,必然可以得出长度为 I+1 的最短。
不存在负环时,最短路经过的边数显然不超过 N-1,这也告诉我们第 N 次迭 代如果发现可以继续更新则必然存在负

这道题就可以先初始化起点在源点有货币的钱数,然后其他点为0,每条边u->v按着率算一下,就是v能通过一些货币最后从原有货币转过来多少。如果出现所谓的负环,即出现了n次下来还能更新,就说明一定能让钱越来越多,一定能回去的更多===

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 150;
int n,m,s;
double v,dis[maxn*2];
bool flag;
int tot,head[maxn*2];
queue<int>q;

struct node{
    int u,v,next;
    double c,r;
}edges[maxn*2];

void add(int u,int v,double ra,double ca,double rb,double cb){
    edges[tot].u = u;edges[tot].v = v;edges[tot].c = ca;edges[tot].r = ra;edges[tot].next = head[u];head[u] = tot++;
    edges[tot].u = v;edges[tot].v = u;edges[tot].c = cb;edges[tot].r = rb;edges[tot].next = head[v];head[v] = tot++;
}

void init(){
    tot = 0;
    memset(dis,0,sizeof(dis));
    memset(head,-1,sizeof(head));
    for(int i = 0 ; i < m ; i++){
        int a,b;
        double ra,ca,rb,cb;
        scanf("%d%d%lf%lf%lf%lf",&a,&b,&ra,&ca,&rb,&cb);
        add(a,b,ra,ca,rb,cb);
    }
}

bool relax(int k){
    double t = edges[k].r * (dis[edges[k].u]-edges[k].c);
    if(t > dis[edges[k].v]){
        dis[edges[k].v] = t;
        return true;
    }
    return false;
}

bool bell(){
    dis[s] = v;
    for(int i =  0 ; i < n ; i++){
        flag = false;
        for(int k = 0 ; k < tot ; k++){
            if(relax(k))    flag = true;
        }
        if(dis[s] > v)  return true;
        if(!flag)   return false;
    }
    return true;
}

int main(){
    while(~scanf("%d%d%d%lf",&n,&m,&s,&v)){
        init();
        if(bell())
            printf("YES\n");
        else
            printf("NO\n");
    }
}

spfa是bellman的改进版,因为很多时候跑了所有边,松弛不了几条,于是就可以用队列,每次都通过之前被松弛的点松弛其余点,如果一个点入队超过n次,一定有环,因为他最多被其余n-1个点更新,这道题也是如此,如果这个点入队了n次,说明有环出现,就可以直接返回yes了,其余的没有环的话,看看源点是否更新即可

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 150;
int n,m,s;
double v,dis[maxn*2];
bool flag,inq[maxn*2];
int tot,head[maxn*2],num[maxn*2];
queue<int>q;

struct node{
    int u,v,next;
    double c,r;
}edges[maxn*2];

void add(int u,int v,double ra,double ca,double rb,double cb){
    edges[tot].u = u;edges[tot].v = v;edges[tot].c = ca;edges[tot].r = ra;edges[tot].next = head[u];head[u] = tot++;
    edges[tot].u = v;edges[tot].v = u;edges[tot].c = cb;edges[tot].r = rb;edges[tot].next = head[v];head[v] = tot++;
}

void init(){
    tot = 0;
    memset(inq,false,sizeof(inq));
    memset(dis,0,sizeof(dis));
    memset(head,-1,sizeof(head));
    memset(num,0,sizeof(num));
    for(int i = 0 ; i < m ; i++){
        int a,b;
        double ra,ca,rb,cb;
        scanf("%d%d%lf%lf%lf%lf",&a,&b,&ra,&ca,&rb,&cb);
        add(a,b,ra,ca,rb,cb);
    }
}

bool relax(int k){
    double t = edges[k].r * (dis[edges[k].u]-edges[k].c);
    if(t > dis[edges[k].v]){
        dis[edges[k].v] = t;
        return true;
    }
    return false;
}

bool spfa(){
    q.push(s);
    dis[s] = v;
    inq[s] = true;
    num[s]++;
    while(!q.empty()){
        int tmp= q.front();
        q.pop();
        inq[tmp] = false;
        for(int k = head[tmp];k!=-1;k = edges[k].next){
            if(relax(k)&&inq[edges[k].v]==false){
                inq[edges[k].v] = true;
                q.push(edges[k].v);
                num[edges[k].v]++;
                if(num[edges[k].v] > n) return true;
     &     }
        }
    }
    if(dis[s]>v)    return true;
    return false;
}

int main(){
    while(~scanf("%d%d%d%lf",&n,&m,&s,&v)){
        init();
        if(spfa())
            printf("YES\n");
        else
            printf("NO\n");
    }
}

spfa 0ms bellman 15ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值