zzuli 2179: 紧急营救(有一条边权值可变为0的最短路)

题目链接

Description

冷锋在非洲完成任务后回到了狼牙特种作战部队。我们知道在战狼二结尾,冷锋正在北极执行任务,而部队发现了龙小云在c国的消息,让冷锋尽快赶往c国。我们知道现在地球上共有n个国家和地区,编号分别为1,2,3…n。国家与国家之间的可能通航班(可能不止一次),也可能没有通航班。共有m次航班,冷锋已经知道了这m次航班的信息(起点 终点,时间)北极的编号是1,c国的编号是n。
而冷峰身为超级英雄一样的的存在,他有一次将航班的时间降为零的能力。
Input

样例数t(t<=10),接下来又t组样例。 每组样例先输入n , m(n<=1000 , m<=n*(n-1)/2)。

下面m行航班的信息,分别为start , end , time(time <= 100000).
Output

对每组样例,输出冷锋到达c国的最短时间,若不能到达输出 “Impossible”。
Sample Input

3
3 1
1 2 1
4 3
1 2 4
2 3 1
2 4 4
3 3
1 2 100000
2 3 1
1 3 2
Sample Output

Impossible
4
0

真的是不想说,这出题人语文怕是白学了,题目描述路线是航班,还有起点,终点,按理说是有向图,然而这个题竟然是无向图,(鉴于出题人已经修改题面,这里就不吐槽这么狠了。)

思路就是先跑出起点1到其他任意点的距离存到dis2里面,然后再跑一下n到其他任意点的距离存到dis1里面,然后再枚举每条边,判断把这条边的权值置为0,是否能让结果更小,由于是无向图,所以对于u-v这条边,res = min(res,dis1[u]+dis2[v],dis1[v]+dis2[u]);

这题还是非常好的,开阔思路,如果这是有向图的话,我们的做法就是同样先跑出1到其他任意点的距离存到dis2里面,然后再把边反向存一下,再跑出任意一个点到n的距离,然后再枚举每条u-v边,即
res = min(res,dis2[u]+dis1[v]);

题目代码如下:

#include

using namespace std;
const int MAX_V = 1010;
const int MAX_E = 500010;
const int INF = 0x3f3f3f3f;
int x[MAX_E],y[MAX_E],z[MAX_E];
int dis1[MAX_V],dis2[MAX_V];
bool used[MAX_V];
struct Edge{
    int to;
    int cost;
}es;
vector G[MAX_V];
int n,m;
void spfa(int a_){
    fill(dis1+1,dis1+1+n,INF);
    memset(used,false,sizeof(used));
    dis1[a_] = 0;
    queue<int> q;
    q.push(a_);used[a_] = true;
    while(!q.empty()){
        int k = q.front();q.pop();
        int len = G[k].size();
        for(int i=0;iif(dis1[es.to] > dis1[k] + es.cost){
                dis1[es.to] = dis1[k] + es.cost;
                if(!used[es.to]){
                    q.push(es.to);
                }
            }
        }
        used[k] = false;
    }
}
int main(void){
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
            G[i].clear();
        for(int i=1;i<=m;i++){
            scanf("%d %d %d",&x[i],&y[i],&z[i]);
            es.to = y[i];es.cost = z[i];
            G[x[i]].push_back(es);
            es.to = x[i];
            G[y[i]].push_back(es);
        }
        spfa(1);
        for(int i=1;i<=n;i++){
            dis2[i] = dis1[i];
        }
        spfa(n);
        int res = INF;
        for(int i=1;i<=m;i++){
            res = min(res,dis2[x[i]]+dis1[y[i]]);
            res = min(res,dis2[y[i]]+dis1[x[i]]);
        }
        if(res == INF)
            printf("Impossible\n");
        else
            printf("%d\n",res);
    }
    return 0;
}

过了将近一年,发现这个题目应该是一道裸的分层图,不过这里是特殊情况,只有将一条边变为0,如果是将不超过K边变为0的题,就是一道标准的分层图题目了,做法也很简单,就是建立一个K层的图,没层的连接是原来的边,不过这里权值为0

下面是我用分层图做法的代码,因为这里K=1,所以只需要建立两层就可以了。
代码如下:

#include

using namespace std;
typedef long long ll;
const int MAX_V = 1010;
const int MAX_E = 500010;
const ll INF = 1e16+10;
class Edge{
public:
    int to,nex;
    ll cost;
    Edge();
    Edge(int _to,int _nex,ll _cost);
};
Edge edg[10*MAX_E];
int head[3*MAX_V],cnt;
ll dis[3*MAX_V];
void init(){
    memset(head,-1,sizeof(head));
    cnt = 0;
}
void add_Edge(int u,int v,ll w){
    edg[cnt] = Edge(v,head[u],w);
    head[u] = cnt++;
}
int N,M;
class Rule{
public:
    bool operator() (const int A,const int B){
        return dis[A] > dis[B];
    }
};
void Dijkstra(int s,int t){
    for(int i=0;i<=2*N;++i) dis[i] = INF;
    priority_queue<int,vector<int>,Rule > que;
    dis[s] = 0;
    que.push(s);
    while(!que.empty()){
        int u = que.top();que.pop();
        for(int i=head[u];~i;i=edg[i].nex){
            Edge &e = edg[i];
            if(dis[e.to] > dis[u] + e.cost){
                dis[e.to] = dis[u] + e.cost;
                que.push(e.to);
            }
        }
    }
    ll res = min(dis[N],dis[2*N]);
    if(res == INF){
        printf("Impossible\n");
    }
    else{
        printf("%lld\n",res);
    }
}
int main(void){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d",&N,&M);
        int u,v;
        ll w;
        for(int i=1;i<=M;++i){
            scanf("%d%d%lld",&u,&v,&w);
            add_Edge(u,v,w);
            add_Edge(v,u,w);
            add_Edge(u+N,v+N,w);
            add_Edge(v+N,u+N,w);
            add_Edge(u,v+N,0);
            add_Edge(v,u+N,0);
        }
        Dijkstra(1,N);
    }
    return 0;
}
Edge::Edge(){
    to = nex = 0;
    cost = 0;
}
Edge::Edge(int _to,int _nex,ll _cost){
    to = _to;
    nex = _nex;
    cost = _cost;
}

对于多层的情况,可以看下洛谷 2939

你可能感兴趣的:(图论大师的成长之路)