常用技巧:分层图

一般见于图论搜索的题目中。题目中提供了一些操作,每个点可以选择进行操作。由于操作次数的不同,会产生非常多的情况,如果考虑何时使用操作,情况会更多。如果将在图上求解最短路看成是在二维平面上进行的,引入进行操作的次数 k 做为第三维,那么这个三维空间就理应可以包含所有的情况,便可以在这个三维空间上解决问题。

每一层图与原图是一样的。对于可选择的操作,在每个点上新添单向边,代表进行操作,可以转移到新图也就是下一层图当中。k个操作产生k+1层图,第 i 层图代表进行了 i 次操作后的图。

每相邻两层图之间的联系,应该展现状态的转移,体现出操作是发生在哪,结果又是到了哪。根据操作的特点,对新的有向边的边权进行修改

分层图构造完毕后,也就相当于普通的无向图/有向图。在这之上求最短路或者遍历即可。参考例题:洛谷P4568P2939P1073。下面是P4568代码

#include<bits/stdc++.h>
using namespace std;
int n,m,k,s,t,dis[200005];;
struct edge
{
    int next,cost;
};
vector<edge> edges[200005];
struct node
{
    int loc,dis;
    bool operator < (const node &x) const
    {
        return x.dis<dis;
    }
};
priority_queue<node> q;
bool mark[200005];

void dij()
{
    memset(dis,0x3f3f3f3f,sizeof(dis));
    q.push((node){s,0});
    dis[s]=0;
    while(!q.empty()){
        node now=q.top();
        q.pop();
        if(mark[now.loc]) continue;
        mark[now.loc]=true;
        for(int i=0;i<edges[now.loc].size();i++){
            int next=edges[now.loc][i].next;
            if(mark[next]) continue;
            int cost=edges[now.loc][i].cost;
            if(dis[next]>now.dis+cost){
                dis[next]=now.dis+cost;
                q.push((node){next,dis[next]});
            }
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    scanf("%d%d",&s,&t);
    for(int i=1;i<=m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        for(int j=0;j<=k;j++){
            edges[a+j*n].push_back((edge){b+j*n,c});
            edges[b+j*n].push_back((edge){a+j*n,c});
            if(j!=k){
                edges[a+j*n].push_back((edge){b+j*n+n,0});
                edges[b+j*n].push_back((edge){a+j*n+n,0});
            }
        }
    }
    for(int i=0;i<k;i++){
        edges[t+i*n].push_back((edge){t+i*n+n,0});
    }
    t=t+k*n;
    n=(k+1)*n;
    dij();
    printf("%d\n",dis[t]);
    return 0;
}

 

posted @ 2020-10-09 15:25  太山多桢  阅读(259)  评论(0编辑  收藏  举报