POJ1806
Bellman-Ford算法的反向利用。
可以把初始兑换的货币看成源点,采用bellman-ford进行求解,若可以实现要求,则说明图中存在可以无限增大的环,这个可以通过bellman-ford算法判断环是否存在求出来,若在求解过程中发现已经兑换回原货币,并且钱比之前多,则可以直接退出算法。由于兑换过程中每种货币值必须为非负的,因此可以把所有初始路径长度设置为0,按照兑换方式对边进行松弛。
上代码:
import java.util.Arrays;
import java.util.Scanner;
/*
* 题意 : 就是套汇的问题,汇率Rab, 增加了一个手续费 Cab 。。。。。。。每次的结果是 (本金 - 手续费) * 汇率,
* 而且一个人拥有的钱的类型是已知的,拥有的value 钱的个数也是已知的, 问你能不能增值。
当成 每个节点之间都可以来回的图,但是 来回的权值不一样,汇率和手续费不一样,分别保存a到b的和b到a的手续费和汇率
才能是关键,其实也是个水题。。。。。
输入 :
3 2 1 20.0 //钱种类个数 汇率的个数,拥有第几种钱, 拥有多少钱
1 2 1.00 1.00 1.00 1.00 //钱a, 钱b, rab, cab, rba, cba
2 3 1.10 1.00 1.10 1.00 //rab 为汇率 cab为汇率
输出:
YES
*
*
* */
public class POJ1806 {
static int n; // 货币种数
static int m; // 兑换点数量
static int s; // 持有第 s种 货币
static double v; // 持有s 货币的本金
static Edge[] rate;
static Edge[] commissino;
static double[] value;
public static final double EPS = 1E-8;// 10 的 -8 次方
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
s = sc.nextInt() - 1;// 减去 1 是因为在数组中第一个数的角标为 0
v = sc.nextDouble();
int a;
int b;
double rab;
double cab;
double rba;
double cba;
rate = new Edge[m * 2];
commissino = new Edge[m * 2];
value = new double[n];
for (int i = 0; i < 2 * m; i++) {
a = sc.nextInt() - 1;
b = sc.nextInt() - 1;
rab = sc.nextDouble();
cab = sc.nextDouble();
rba = sc.nextDouble();
cba = sc.nextDouble();
rate[i] = new Edge();
rate[i].s = a;//这里是a
rate[i].e = b;
rate[i].v = rab;
commissino[i] = new Edge();
commissino[i].s = a;
commissino[i].e = b;
commissino[i].v = cab;
i++;
rate[i] = new Edge();
rate[i].s = b;//跟上面的不一样哦 这里是b
rate[i].e = a;//
rate[i].v = rba;
commissino[i] = new Edge();
commissino[i].s = b;//
commissino[i].e = a;//
commissino[i].v = cba;
}
new POJ1806().solve();
}
public void solve() {
if (bellman_ford(s, rate, commissino)) {
System.out.println("YES");
} else {
System.out.println("NO");
}
}
private static boolean bellman_ford(int ss, Edge[] rat, Edge[] com) {
// TODO Auto-generated method stub
Arrays.fill(value, 0);// 快速给value数组元素赋值 为0
value[ss] = v;
boolean released;
while (value[ss] <= v + EPS) {
released = false;
for (int i = 0; i < 2 * m; i++) {
if (value[rat[i].e] + EPS < (value[rat[i].s] - com[i].v) * rat[i].v) {//这里反向利用Bellman_Ford算法 来判断是否无限增大
value[rat[i].e] = (value[rat[i].s] - com[i].v) * rat[i].v;
released = true;
//System.out.println(Arrays.toString(value));
}
}
if (!released) {
//System.out.println(true);
return value[ss] > v + EPS;
}
}
//System.out.println(value[s]);
return true;
}
}
class Edge {
int s;
int e;
double v;
}