P2176 [USACO14FEB]路障Roadblock

题目描述

每天早晨,FJ从家中穿过农场走到牛棚。农场由 N 块农田组成,农田通过 M 条双向道路连接,每条路有一定长度。FJ 的房子在 1 号田,牛棚在 N 号田。没有两块田被多条道路连接,以适当的路径顺序总是能在农场任意一对田间行走。当FJ从一块田走到另一块时,总是以总路长最短的道路顺序来走。

FJ 的牛呢,总是不安好心,决定干扰他每天早晨的计划。它们在 M 条路的某一条上安放一叠稻草堆,使这条路的长度加倍。牛希望选择一条路干扰使得FJ 从家到牛棚的路长增加最多。它们请你设计并告诉它们最大增量是多少。

输入输出格式

输入格式:

第 1 行:两个整数 N, M。

第 2 到 M+1 行:第 i+1 行包含三个整数 A_i, B_i, L_i,A_i 和 B_i 表示道路 i 连接的田的编号,L_i 表示路长。

输出格式:

第 1 行:一个整数,表示通过使某条路加倍而得到的最大增量。

输入样例#1:  复制
5 7
2 1 5
1 3 1
3 2 8
3 5 7
3 4 3
2 4 7
4 5 2
输出样例#1:  复制
2


刚看完第一段以为是一道普通的最短路,看到后面。emmmmm...
同学提醒说是最短路径树,然而并不会那么高深的东西qaq,只能用简单的思想去捣鼓了。
听了别人的想法,Dijksra中,在每次松弛时时记录一下v和他的前驱节点u,这样就能逆向回溯出从x到n的最短路径了。
同时记得区分第一次和之后的Dijkstra,注意只有第一次Dijkstra时记录松弛的路径,以防之后错误修改第一次时记录好的最短路径;
暴力最短路径中每一条倍增所产生的影响,然后取其中的最大值与第一次求差,得出结果。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int MAX_V= 205;
const int MAX_E= 20000;
const int inf= 0x3f3f3f3f;
struct ENode
{
    int to;
    int w;
    int next;
};
ENode Edegs[MAX_E];
int Head[MAX_V], cnt;         //cnt: 边计数器;
int Dis[MAX_V];
int Front[MAX_V];             //Front[v]: 最短路径中v的前置节点u;
pair<int, int> E_double_cost; //连接这两点的边,其权值加倍;
bool flag;                    //flag记录是否为初次求最短路;
void Add_Edge_0 (int a, int b, int w)
{
    //有向图建边;
    ++ cnt;
    Edegs[cnt].to= b;
    Edegs[cnt].w= w;
    Edegs[cnt].next= Head[a];
    Head[a]= cnt;
}
void Add_Edge_1 (int a, int b, int w)
{
    //无向图建边;
    ++ cnt;
    Edegs[cnt].to= b;
    Edegs[cnt].w= w;
    Edegs[cnt].next= Head[a];
    Head[a]= cnt;
    ++ cnt;
    Edegs[cnt].to= a;
    Edegs[cnt].w= w;
    Edegs[cnt].next= Head[b];
    Head[b]= cnt;
}
struct cmpx
{
    bool operator()(int &a, int &b)const
    {
        return Dis[a]- Dis[b]> 0;
    }
};
void Dijkstra(int x)
{
    priority_queue<int, vector<int>, cmpx> q;
    memset(Dis, inf, sizeof(Dis));
    Dis[x]= 0;
    Front[x]= -1;  //x 的前驱节点为-1;
    q.push(x);

    while (! q.empty())
    {
        int u= q.top();
        q.pop();
        for (int k= Head[u]; k!= -1; k= Edegs[k].next )
        {
            int v= Edegs[k].to;
            if(!flag && u== E_double_cost.first&& v== E_double_cost.second )
            {
                //边E(u->v)的花费被加倍了;
                if (Dis[v]> Dis[u]+ Edegs[k].w+ Edegs[k].w )
                {
                    Dis[v]= Dis[u]+ Edegs[k].w+ Edegs[k].w;
                    q.push(v);
                }
            }
            else
            {
                if (Dis[v]> Dis[u]+ Edegs[k].w )
                {
                    Dis[v]= Dis[u]+ Edegs[k].w;
                    if (flag) Front[v]= u;
                    q.push(v);
                }
            }
        }
    }
}

int main()
{
    int n, m;
    cin >>n >>m;
    int a, b, w;
    cnt= -1;
    memset(Head, -1, sizeof(Head));
    for (int i= 0; i< m; i ++)
    {
        cin >>a >>b >>w;
        Add_Edge_1(a, b, w);
    }
    flag= true;
    Dijkstra(1);
    flag= false;
    int tmp= Dis[n];   //第一次时到n的最短路距离;
    int far= tmp;      //保存修改后到n的最短路距离;
    for (int i= n; i!= 1; i= Front[i] )
    {
        E_double_cost.first= Front[i];
        E_double_cost.second= i;
        Dijkstra(1);
        far= max(far, Dis[n]);
    }
    cout <<far- tmp <<endl;
    return 0;
}
 

猜你喜欢

转载自www.cnblogs.com/Amaris-diana/p/10555354.html