分层图练习

P4568 [JLOI2011] 飞行路线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

//////////////////////////////////////////////////////法一:分层图
int n,m,k;
int s,t;
const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[10004*12];   //开多层,一定要开大点!!10004*11都是RE的
priority_queue<pair<int,int>> pq;
int dis[10004*12],vis[10004*12];
void dijkstr(int s){
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    pq.emplace(0,s);
    while(pq.size()){
        int from=pq.top().second;
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.first,weight=v.second;
            if(dis[to]>dis[from]+weight){
                dis[to]=dis[from]+weight;
                pq.emplace(-dis[to],to);
            }
        }
    }
}
void solve(){
    cin>>n>>m>>k;
    cin>>s>>t;
    for(int i=1;i<=m;i++){
        int u,v,w; cin>>u>>v>>w;
        for(int j=0;j<=k;j++){           //重边不必处理--key:j=[0,k]--到k是因为k的本层要建图,但是k不需要连向k+1层,连了也没wa。
            vct[u+j*n].emplace_back(v+j*n,w);           //本层是双向的
            vct[v+j*n].emplace_back(u+j*n,w);
            if(j!=k) vct[u+j*n].emplace_back(v+(j+1)*n,0);         //下层的单向的
            if(j!=k) vct[v+j*n].emplace_back(u+(j+1)*n,0);
        }
    }
    dijkstr(s);
    int ans=INT_MAX;
    for(int i=0;i<=k;i++)
        ans=min(ans,dis[t+i*n]);
    cout<<ans;
}
////////////////////////////////////////////////////法二:dp
int n,m,k;
int s,t;
typedef struct myp{
    int d,node;
    int t;
    friend bool operator < (const myp a,const myp b){
        return a.d>b.d;         //大于号才是!!小根堆!!!
    }
}myp;
const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[10004];
priority_queue<myp> pq;
int dis[10004][15],vis[10004][15];
void dijkstr(int s){
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s][0]=0;
    pq.emplace((myp){0,s,0});
    while(pq.size()){
        int from=pq.top().node;
        int take=pq.top().t;
        pq.pop();
        if(vis[from][take]) continue;
        vis[from][take]=1;
        for(auto v:vct[from]){
            int to=v.first,weight=v.second;
            if(take+1<=k){
                if(dis[to][take+1]>dis[from][take]){
                    dis[to][take+1]=dis[from][take];
                    pq.emplace((myp){dis[to][take+1],to,take+1});
                }
            }
            if(dis[to][take]>dis[from][take]+weight){
                dis[to][take]=dis[from][take]+weight;
                pq.emplace((myp){dis[to][take],to,take});
            }
        }
    }
}
void solve(){
    cin>>n>>m>>k;
    cin>>s>>t;
    for(int i=1;i<=m;i++){
        int u,v,w; cin>>u>>v>>w;
        vct[u].emplace_back(v,w);
        vct[v].emplace_back(u,w);
    }
    dijkstr(s);
    int ans=INT_MAX;
    //hack数据--不能单纯输出dis[t][k],类似限高杆,不一定用完免费的会更值当!!!所以要在[0,k]中取最小值。
    //k>m0,m0为s到t需要最少的边,如果要用完k,可能会导致跑多余的路程.
    for(int i=0;i<=k;i++) ans=min(ans,dis[t][i]);
    cout<<ans;
}

P8724 [蓝桥杯 2020 省 AB3] 限高杆 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

////////////////////////////////////////////法一:分层图
int n,m;
const int inf=0x3f3f3f3f;
vector<pair<int,int>> vct[30004];
priority_queue<pair<int,int>> pq;
int dis[30004],vis[30004];
void dijkstr(int s){
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s]=0;
    pq.emplace(0,s);
    while(pq.size()){
        int from=pq.top().second;
        pq.pop();
        if(vis[from]) continue;
        vis[from]=1;
        for(auto v:vct[from]){
            int to=v.first,weight=v.second;
            if(dis[to]>dis[from]+weight){
                dis[to]=dis[from]+weight;
                pq.emplace(-dis[to],to);
            }
        }
    }
}
void solve(){               
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w,c; cin>>u>>v>>w>>c;
        //用以下方式建一个立体的三层图,跑正常的dijkstra即可。恢复一条边的最短距离为dis[2*n];恢复两条边的最短距离为dis[3*n];
        //此题恢复的边一定会用上那条边,不一定恢复两条边会比恢复一条边短,或比不恢复边短。取min即可。
        if(c==0){               //没有障碍,三层都在本层正常建边
            vct[u].emplace_back(v,w);  //第一层
            vct[v].emplace_back(u,w);
            vct[u+n].emplace_back(v+n,w);  //第二层
            vct[v+n].emplace_back(u+n,w);
            vct[u+2*n].emplace_back(v+2*n,w);  //第三层
            vct[v+2*n].emplace_back(u+2*n,w);
        }
        else{       //有障碍,第一层连向第二层,第二层连向第三层。
//            vct[u].emplace_back(v+n,w);   //这4行是错的。
//            vct[v+n].emplace_back(u,w);
//            vct[u+n].emplace_back(v+2*n,w);
//            vct[v+2*n].emplace_back(u+n,w);
            vct[u].emplace_back(v+n,w);   //单向的!!一层向下一层!!,否则可能会从下层更新上层的点
            vct[v].emplace_back(u+n,w);
            vct[u+n].emplace_back(v+2*n,w);
            vct[v+n].emplace_back(u+2*n,w);
        }
    }
    dijkstr(1);
    cout<<dis[n]-min({dis[n],dis[2*n],dis[3*n]});   //不一定恢复的边越多越短!直接取三个的最小值准没错!!
}
/////////////////////////////////////////////////法二:dp
int n,m;
typedef struct myp1{
    int v,w,typ;
}myp1;
typedef struct myp2{
    int d,node,take;
    friend bool operator <(const myp2 a,const myp2 b){    //只能重载小于号!!
        return a.d>b.d;      //按d的从小到大排序
    }
}mpy2;
const int inf=0x3f3f3f3f;
vector<myp1> vct[10004];
priority_queue<myp2> pq;
//priority_queue<pair<int,pair<int,int>>> pq;
int dis[10004][3],vis[10004][3];    //dis[i][j]:已经用了j次免费机会的情况下,到达i的最小距离
void dijkstra(int s){
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[s][0]=0;
    pq.emplace((myp2){0,s,0});
    while(pq.size()){
        int from=pq.top().node;
        int take=pq.top().take;
        pq.pop();
        if(vis[from][take]) continue;
        vis[from][take]=1;
        for(auto v:vct[from]){
            int to=v.v,weight=v.w,typ=v.typ;
            if(take+typ>2) continue;
            if(dis[to][take+typ]>dis[from][take]+weight){       //dis[to][take+typ] 从 dis[from][take]+weight转移!!
                dis[to][take+typ]=dis[from][take]+weight;
                pq.emplace(myp2{dis[to][take+typ],to,take+typ});
            }
        }
    }
}
void solve(){         //不建分层图,用动态规划思想.
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v,w,t; cin>>u>>v>>w>>t;
        vct[u].emplace_back((myp1){v,w,t});
        vct[v].emplace_back((myp1){u,w,t});
    }
    dijkstra(1);
    cout<<dis[n][0]-min({dis[n][0],dis[n][1],dis[n][2]});     //不一定恢复的边越多越短!直接取三个的最小值准没错!!
}

ps:似乎分层图都可以用dp来做。

posted @ 2024-04-23 22:06  osir  阅读(1)  评论(0编辑  收藏  举报