【题解】最短路二题

版权声明:Fashion Education https://blog.csdn.net/ModestCoder_/article/details/82729580

LuoGu1576:最小花费
题目描述
在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收到100元。

输入输出格式
输入格式:
第一行输入两个正整数n,m,分别表示总人数和可以互相转账的人的对数。

以下m行每行输入三个正整数x,y,z,表示标号为x的人和标号为y的人之间互相转账需要扣除z%的手续费 (z<100)。

最后一行输入两个正整数A,B。数据保证A与B之间可以直接或间接地转账。

输出格式:
输出A使得B到账100元最少需要的总费用。精确到小数点后8位。
输入样例
3 3
1 2 1
2 3 2
1 3 3
1 3
输出样例
103.07153164

1<=n<=2000,m<=100000

【题解】
最短路题,不裸,披了一件薄薄的浮点数运算外套~~
SPFA不稳定,用dijkstra+heap
因为是算初始钱的最小值,所以从终点开始搞,用小学百分数运算的知识

Code:

type
    node = record
        id:longint;
        num:real;
    end;

var
    heap:array[0..200000] of node;
    edge:array[0..200000] of record
        t,len,next:longint;
    end;
    head:array[0..200000] of longint;
    vis:array[0..200000] of boolean;
    dis:array[0..200000] of real;
    x:real;
    n,m,i,y,s,t,num,e,len,z:longint;

procedure add(x,y,z:longint);

begin
    inc(num);
    edge[num].t := y;
    edge[num].len := z;
    edge[num].next := head[x];
    head[x] := num;
end;

procedure swap(var x,y:node);
var
    tmp:node;

begin
    tmp := x; x := y; y := tmp;
end;

procedure pop;
var
    i,x:longint;

begin
    heap[1] := heap[len]; dec(len); i := 1;
    while (i << 1) <= len do
    begin
        if ((i << 1) or 1 > len) or (heap[i << 1].num < heap[(i << 1) or 1].num) then
            x := i << 1 else x := (i << 1) or 1;
        if heap[i].num > heap[x].num then
        begin
            swap(heap[i],heap[x]);
            i := x;
        end else break;
    end;
end;

procedure push(x:real; y:longint);
var
    i:longint;

begin
    inc(len);
    heap[len].num := x; heap[len].id := y;
    i := len;
    while i > 1 do
    begin
        if heap[i].num < heap[i >> 1].num then
        begin
            swap(heap[i],heap[i >> 1]);
            i := i >> 1;
        end else break;
    end;
end;

begin
    readln(n,m);
    for i := 1 to m do
    begin
        readln(s,t,z);
        add(s,t,z); 
        add(t,s,z);//双向边,我因此wa掉了一次
    end;
    readln(s,t);
    for i := 1 to n do dis[i] := maxlongint >> 1;
    dis[t] := 100;
    len := 1;
    heap[1].num := 100; heap[1].id := t;
    while len > 0 do//模板
    begin
        x := heap[1].num; y := heap[1].id;
        pop;
        if vis[y] then continue;
        vis[y] := true;
        i := head[y];
        while i <> 0 do
        begin
            e := edge[i].t;
            if dis[e] > (x / (100 - edge[i].len) * 100) then//百分数的运算
            begin
                dis[e] := x / (100 - edge[i].len) * 100;
                push(dis[e],e);
            end;
            i := edge[i].next;
        end;
    end;
    writeln(dis[s]:0:8);
end.

LuoGu1119:灾后重建
题目描述
给出B地区的村庄数N,村庄编号从0到N-1,和所有M条公路的长度,公路是双向的。并给出第i个村庄重建完成的时间t_i,你可以认为是同时开始重建并在第t_i天重建完成,并且在当天即可通车。若t_i为0则说明地震未对此地区造成损坏,一开始就可以通车。之后有Q个询问(x, y, t),对于每个询问你要回答在第t天,从村庄x到村庄y的最短路径长度为多少。如果无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未重建完成 ,则需要返回−1。

输入格式:
第一行包含两个正整数N,M,表示了村庄的数目与公路的数量。
第二行包含N个非负整数t_0, t_1,…, t_{N-1},表示了每个村庄重建完成的时间,数据保证了t_0 ≤ t_1 ≤ … ≤ t_{N-1}
接下来M行,每行3个非负整数i, j, w,w为不超过10000的正整数,表示了有一条连接村庄i与村庄j的道路,长度为w,保证i≠j,且对于任意一对村庄只会存在一条道路。
接下来一行也就是M+3行包含一个正整数Q,表示Q个询问。
接下来Q行,每行3个非负整数x, y, t询问在第t天,从村庄x到村庄y的最短路径长度为多少,数据保证了t是不下降的。

输出格式:
共Q行,对每一个询问(x, y, t)输出对应的答案,即在第t天,从村庄x到村庄y的最短路径长度为多少。如果在第t天无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未修复完成,则输出-1。

输入样例
4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4
输出样例
-1
-1
5
4

对于100%的数据,有N≤200,M≤N×(N−1)/2,Q≤50000,所有输入数据涉及整数均不超过100000。
【题解】
此题难度提高+省选-,个人认为应该降为普及+提高-
思路很好想,看到n的范围加题意,知道用floyd
然后给询问t从小到大做,每次激活点,意思是,有一些点前一次询问时还没修好,现在修好了,那么可以参与最短路计算了。
激活的点当的是中转站,而且每个点只做一次,所以dis数组保存的是当前多源最短路

Code:

var
    dis:array[0..1000,0..1000] of int64;
    vis:array[0..100000] of boolean;
    t:array[0..100000] of longint;
    n,m,i,j,k,x,y,z,q:longint;

begin
    readln(n,m);
    for i := 0 to n - 1 do
        for j := 0 to n - 1 do
            if i <> j then dis[i][j] := maxlongint;
    for i := 0 to n - 1 do read(t[i]);
    for i := 1 to m do
    begin
        readln(x,y,z);
        dis[x][y] := z;
        dis[y][x] := z;
    end;
    readln(m);
    for q := 1 to m do
    begin
        readln(x,y,z);
        for k := 0 to n - 1 do
            if not vis[k] and (t[k] <= z) then
            begin
                for i := 0 to n - 1 do
                    for j := 0 to n - 1 do
                        if (i <> j) and (i <> k) and (k <> j) and (dis[i][j] > dis[i][k] + dis[k][j]) then
                            dis[i][j] := dis[i][k] + dis[k][j];
                vis[k] := true;
            end;
        if (t[x] > z) or (t[y] > z) or (dis[x][y] >= maxlongint) then
            writeln(-1) else writeln(dis[x][y]);
    end;
end.

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/82729580