Hile每日算法-3.29-分层建图

分层建图

首先来一道题,题意是这样的:

给定一张有向图(游戏地图),一对起点和终点,每个点代表一个城市,你从起点开车到终点,每次在两个城市间移动需要1h。每个点(城市)都有五种可能的情况:

1.该点没有任何道具;

2.该点有阻碍物需要停止1h;

3.到达该点时游戏失败(保证起点和终点不为3);

4.该点有氮气,接下来连续两次移动速度加倍(倍数不可叠加,次数可以);

5.该点有沙子,接下来连续两次移动速度减半(同上);

求从起点到终点最快用时,如果不能到达终点输出-1.

分析题目,单源最短路径,很显然就该用 SPFA dijkstra算法。对于各类点,遇到1不管,2的话将该点的出边权值(用时)加一,3的话删除连接这个点的所有边。

但是,4和5两种情况怎么处理?怎么实现同一条边权值(用时)加倍和减半的操作,怎么让1、0.5、2三种边权同时存在于一条边上呢?

换个想法,与其纠结在一条边上进行的多次修改,不如把所有可能的权值都建立一条边,也就是说,分层建图

(当然,这并不是什么算法,只是一种建图的技巧/思想。

就这题而言,可以将图分为三层,分别叫做G1G2G3,G1的边权都为1,G2都为0.5,G3都为2,初始在G1层的起点。对于每一个第四类点(氮气),我们可以向G3的同一点连一条无权边(权值为0),然后对于所有和该点距离在2以内的G3层点向G1连一条无权边,如图所示其中G1.1为氮气:

(今天刚学Graphiviz,画的太丑,暂时凑合着看吧)

同理,遇到第五类点作相同处理,这题就变成裸的dijkstra啦。

下面来做道题目体会一下:

bzoj2763: [JLOI2011]飞行路线

给出 n n 个点, m m 条边的无向图,一对起点和终点,给你 k   ( k 10 ) k\ (k\le10) 次将当前边权变为 0 0 的机会,求从起点到终点的最小代价。

由于 k k 比较小,我们可以用分层建图的思想建出 k + 1 k+1 层图,分别表示在各点已经使用了 0 , 1... k 0,1...k 次机会的状态,各层之间转移的边权为 0 0 ,建完之后跑dij即可。

AC代码:

#include <bits/stdc++.h>
#define PII pair<int,int>
#define N 10010
#define ll long long
using namespace std;
bool vis[N*11];//用0~n-1,n~2n-1...kn~(k+1)n-1表示k+1层的点
vector<PII> G[N];//由于各层的连通情况都相同,只需对初始层建图,各层用对n取模访问
int n,m,k,s,t,d[N*11];
void dijkstra(int s)
{
    priority_queue<PII,vector<PII>,greater<PII> > pq;
    pq.push(make_pair(0,s));
    d[s]=0;
    while(!pq.empty())
    {
        int u=pq.top().second;pq.pop();
        if(vis[u])continue;vis[u]=1;
        for(int it=0;it<G[u%n].size();it++)//QAQ,bzoj不能for(auto:G)
        {
            PII i=G[u%n][it];
            int v=i.second+u-u%n;
            if(d[u]+i.first<d[v])//正常松弛
            {
                d[v]=d[u]+i.first;
                pq.push(make_pair(d[v],v));
            }
            if(v+n<(k+1)*n&&d[v+n]>d[u])//到下一层
            {
                d[v+n]=d[u];
                pq.push(make_pair(d[v+n],v+n));
            }
        }
    }
}
int main()
{
    scanf("%d%d%d%d%d",&n,&m,&k,&s,&t);
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back(make_pair(w,v));
        G[v].push_back(make_pair(w,u));
    }
    memset(d,0x3f,sizeof(d));
    dijkstra(s);
    printf("%d",d[k*n+t]);
}
发布了7 篇原创文章 · 获赞 10 · 访问量 434

猜你喜欢

转载自blog.csdn.net/weixin_44700995/article/details/105172910