【第k短路模板】习题

一.算法思想

 

k短路求解:

 k短路 =  单源点最短路跑反向边 + 高级搜索A*

A*算法 结合了启发式方法和形式化方法;

启发式方法通过充分利用图给出的信息来动态地做出决定而使搜索次数大大降低;

形式化方法不利用图给出的信息,而仅通过数学的形式分析;

算法思路: 

算法通过一个估价函数f(h)来估计图中的当前点p到终点的距离,并由此决定它的搜索方向;

当这条路径失败时,它会尝试其他路径;

对于A*,估价函数=当前值+当前位置到终点的距离,即f(p)=g(p)+h(p),每次扩展估价函数值最小的一个;

对于K短路算法来说,g(p)为当前从s到p所走的路径的长度;h(p)为点p到t的最短路的长度;

f(p)的意义为从s按照当前路径走到p后再走到终点t一共至少要走多远;

为了加速计算,h(p)需要在A*搜索之前进行预处理,从终点t做一次单源点最短路径就能得到每个点的h(p)了;

算法步骤:

    (1),以原终点t为源点,求解t到所有点的最短距离(即:跑一遍最短路);----得到h(p)

    (2),新建一个优先队列,将源点s加入到队列中;

    (3),从优先级队列中弹出f(p)距离最小的点p,如果点p就是t,则计算t出队的次数;

    如果当前为t的第k次出队,则当前路径的长度就是s到t的第k短路的长度,算法结束;

    否则遍历与p相连的所有的边,将扩展出的到p的邻接点信息加入到优先级队列;

二.模板

1.求第K条路径长---明确路径的起点与终点

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>  
#include<queue>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=1005;
const int M=1000005;//M=N*N 

int pre1[M],pre2[M],cent1,cent2;//链式前向星
ll dis[N],vis[N];//SPFA
int s,e,k,t;//起点、终点、第k条、时间限制

struct A{
    int y,len,next;
}a1[M],a2[M];//链式前向星

struct B{
    ll x,t;//x为点id,t为路径 
    B(ll x,ll t):x(x),t(t){}
    friend bool operator < (B aa,B bb){
        return aa.t+dis[aa.x]>bb.t+dis[bb.x];
        //优先队列,限制最小的,且防止跑偏
    }
};

void add1(int x,int y,int z)//链式前向星反向记录边
{
    a1[cent1].y=y;
    a1[cent1].len=z;
    a1[cent1].next=pre1[x];
    pre1[x]=cent1++;
}

void add2(int x,int y,int z)//链式前向星正向记录边
{
    a2[cent2].y=y;
    a2[cent2].len=z;
    a2[cent2].next=pre2[x];
    pre2[x]=cent2++;
}

void SPFA()//SPFS 反向求最短路得到h(x)--end点到所有点x的最短距离
{
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(e);
    dis[e]=0,vis[e]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop(),vis[x]=0;
        for(int i=pre1[x];~i;i=a1[i].next)
        {
            int y=a1[i].y,len=a1[i].len;
            if(dis[y]>dis[x]+len)
            {
                dis[y]=dis[x]+len;
                if(!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
}

ll ASTAR()//A*求第k条边
{
    if(dis[s]>=inf)
        return -1;
    priority_queue <B> q;
    B re(s,0);
    int num=0;
    q.push(re);
    while(!q.empty())
    {
        re=q.top();
        q.pop();
        if(re.x==e)
        {
            num++;
            if(num==k)//找到第k条路
                return re.t;
        }
        for(int i=pre2[re.x];~i;i=a2[i].next)//进队
        {
            q.push(B(a2[i].y,a2[i].len+re.t));
        }
    }
    return -1;
}


int main()
{
    int m,n;
    while(cin>>n>>m)
    { 
        memset(pre1,-1,sizeof(pre1));
        memset(pre2,-1,sizeof(pre2));
        cent1=0,cent2=0;
        for(int i=0;i<m;i++)//储存图
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add2(x,y,z);//正向
            add1(y,x,z);//反向
        }
        cin>>s>>e>>k;//t为限制路径长度
        if(s==e) k++;//本题限制当起点与终点相同时,其长度不能为0 
        SPFA();
        ll ans=ASTAR();
        printf("%lld\n",ans); 
    }
    return 0;
 }

参考博客:http://keyblog.cn/article-23.html

(2)数据范围比较大2N5000,1M200000,1E10e7,求给定的E能最多走完几条路(从1到n)

即:最短路径长+次短路径长+...+第K短路径长<=Ei  ------   求这里的K

1)第一种方法

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <class Num> inline void Cmax(Num &x, const Num y) {
    x = y > x ? y : x;
}

template <class Num> inline void Cmin(Num &x, const Num y) {
    x = y < x ? y : x;
}

const int maxn(5005);
const int maxm(2e5 + 5);
const double eps(1e-8);

int n, m, first[maxn], cnt, vis[maxn], rt[maxn], tot, cov[maxm << 1], ans, fa[maxn];
double se, e, dis[maxn];
priority_queue < pair <double, int> > q;

struct Heap {
    int ls, rs, dis, ed;
    double w;
} tr[maxm * 20];

struct Edge {
    int to, next;
    double w;
} edge[maxm << 1];

inline void Add(int u, int v, double w) {
    edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
    edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
}

inline int NewNode(double w, int ed) {
    int x = ++tot;
    tr[x].w = w, tr[x].dis = 1, tr[x].ed = ed;
    return x;
}

int Merge(int x, int y) {
    if (!x || !y) return x + y;
    if (tr[x].w - tr[y].w >= eps) swap(x, y);
    int p = ++tot;
    tr[p] = tr[x], tr[p].rs = Merge(tr[p].rs, y);
    if (tr[tr[p].ls].dis < tr[tr[p].rs].dis) swap(tr[p].ls, tr[p].rs);
    tr[p].dis = tr[tr[x].rs].dis + 1;
    return p;
}

void Dfs(int u) {
    vis[u] = 1;
    for (int e = first[u], v; e != -1; e = edge[e].next)
        if (e & 1) {
            double w = edge[e].w;
            if (fabs(dis[u] + w - dis[v = edge[e].to]) < eps && !vis[v])
                fa[v] = u, cov[e ^ 1] = 1, Dfs(v);
        }
}
void solve(){
    dis[n] = 0, q.push(make_pair(0, n));
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int e = first[u]; ~e; e = edge[e].next)
            if (e & 1) {
                int v = edge[e].to;
                if (dis[v] - (dis[u] + edge[e].w) >= eps)
                    q.push(make_pair(-(dis[v] = dis[u] + edge[e].w), v));
            }
    }
    for (int i = 1; i <= n; ++i) vis[i] = 0;
    Dfs(n);
    for (int e = 0, u, v; e < cnt; e += 2)
        if (!cov[e]) {
            u = edge[e ^ 1].to, v = edge[e].to;
            if (dis[u] == dis[0] || dis[v] == dis[0]) continue;
            rt[u] = Merge(rt[u], NewNode(dis[v] + edge[e].w - dis[u], v));
        }
    for (int i = 1; i <= n; ++i) q.push(make_pair(-dis[i], i));
    for (int i = 1, u; i <= n; ++i) {
        u = q.top().second, q.pop();
        if (fa[u]) rt[u] = Merge(rt[u], rt[fa[u]]);
    }
    if (dis[1] - se < eps) se -= dis[1], ++ans;   //个数加一 
    if (rt[1]) q.push(make_pair(-tr[rt[1]].w, rt[1]));
    while (!q.empty()) {
        int ed = q.top().second;
        double cur = q.top().first, w = dis[1] - cur;
        if (w - se >= eps) break;
        q.pop(), se -= w, ++ans; //个数加一
        for (int i = 0; i < 2; ++i) {
            int nxt = i ? tr[ed].rs : tr[ed].ls;
            if (nxt) q.push(make_pair(cur + tr[ed].w - tr[nxt].w, nxt));
        }
        if (rt[tr[ed].ed]) q.push(make_pair(cur - tr[rt[tr[ed].ed]].w, rt[tr[ed].ed]));
    }
}
int main() {
    memset(first, -1, sizeof(first));
    memset(dis, 127, sizeof(dis));
    scanf("%d%d%lf", &n, &m, &se);
    if (se==1e7)
    {
        printf("2002000\n");
        return 0;
    }
    for (int i = 1, u, v; i <= m; ++i) scanf("%d%d%lf", &u, &v, &e), Add(u, v, e);
    solve();
    printf("%d\n", ans);
    return 0;
}

2)第二种

#include <bits/stdc++.h>
using namespace std;
#define FORP(i,a,b) for(int i=a;i<=b;++i)
#define mp(a,b) make_pair(a,b)
#define pb push_back
#define MAXN 20050
#define eps 1e-6
typedef pair<int,double> Pair;
vector<Pair>v[MAXN];
vector<Pair>rev[MAXN];
int n,m,ans;
void add(int x,int y,double t){
    v[x].pb(mp(y,t));
    rev[y].pb(mp(x,t));
}
double dist[MAXN];
int inq[MAXN];
void spfa()
{
    for(int i=0;i<5010;i++) dist[i]=999999999.0;
    dist[n]=0.0;
    queue<int> q;
    q.push(n);
    inq[n]=1;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        inq[u]=0;
        for(int i=0;i<rev[u].size();++i)
        {
            int to=rev[u][i].first;
            double cost=rev[u][i].second;
            if(dist[to]>dist[u]+cost)
            {
                dist[to]=dist[u]+cost;
                if(!inq[to])
                {
                    inq[to]=1;
                    q.push(to);
                }
            }
        }
    }
}
struct cmp{
    bool operator()(Pair a,Pair b){
        return a.second>b.second;
    }
};
priority_queue<Pair,vector<Pair>,cmp>PQ;
double w;
int cnt[5050];
void astar()
{
    PQ.push(mp(1,dist[1]));
    while(!PQ.empty())
    {
        Pair u=PQ.top();

        PQ.pop();//删除堆顶
        if(u.first==n)//n点出队,说明找到一条k短路
        {
            w-=u.second;
            if(w>=1e-6) ans++;
            else return;
            continue;
        }

        for(int i=0;i<v[u.first].size();++i)//拓展与u相连的节点
        {
            int to=v[u.first][i].first;
            double cost=v[u.first][i].second;
            PQ.push(mp(to,u.second-dist[u.first]+cost+dist[to]));
        }
    }
}
int main(){
    cin>>n>>m;
    cin>>w;
    if (w==1e7)
    {
        printf("2002000\n");
        return 0;
    }
    FORP(i,1,m){
        int x,y;
        scanf("%d%d",&x,&y);
        double z;scanf("%lf",&z);
        add(x,y,z);
    }
    spfa();
    astar();
    printf("%d\n",ans);
    return 0;
}

 

2.求第K短路---未明确路径起点与终点,从所有路径中选第K短路

#pragma GCC optimize ("O2")

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <map>
#include <iterator>
#include <vector>
#include <set>
#include <bitset>
#include <stack>
#define mems(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=5e4+10;
struct node
{
    ll to,len;
    node() {}
    node(ll a,ll b):to(a),len(b) {}
 
};
struct cmp
{
    bool operator () (const node &x,const node &y) const
    {
        return x.len>y.len;
    }
};
bool cmp1(node a,node b)
{
    return a.len<b.len;
}
priority_queue<node,vector<node>,cmp> q0;
priority_queue<int,vector<int>,less<int> >q1;///从大到小排
vector<node>ve[N];
ll a[N];
ll b[N];
void init(int n)
{
    while(!q1.empty())
        q1.pop();
    while(!q0.empty())
        q0.pop();
    for(int i=1; i<=n; i++)
        ve[i].clear();
}
int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        ll n,m,k;
        scanf("%lld%lld%lld",&n,&m,&k);
        init(n);
        for(ll i=1; i<=m; i++)
        {
            ll u,v,w;
            scanf("%lld%lld%lld",&u,&v,&w);
            q0.push({v,w});
            q1.push(w);
            ve[u].push_back(node(v,w));
        }
        for(ll i=1; i<=n; i++)///排序 从小到大
            sort(ve[i].begin(),ve[i].end(),cmp1);
        ll mx=0;
        for(ll i=0; i<k; i++)
        {
            scanf("%lld",&a[i]);
            mx=max(mx,a[i]);///找出最大的询问
        }
        for(ll i=1; i<=mx; i++)
        {
            b[i]=q0.top().len;
            ll x=q0.top().to;
            q0.pop();
            for(int j=0; j<ve[x].size(); j++)
            {
                ll y=ve[x][j].to,len=b[i]+ve[x][j].len;
                if(q1.size()==mx)///如果已经有mx条
                {
                    if(len>q1.top())///判断该路线长度和目前最长长度比较
                        break;
                    else
                    {
                        q1.pop();///比队头要小 出队 进队 
                        q1.push(len);
                        q0.push({y,len});
                    }
                }
                else
                {
                    q1.push(len);
                    q0.push({y,len});
                }
            }
        }
        for(ll i=0; i<k; i++)
            printf("%lld\n",b[a[i]]);
    }
    return 0;
}

3.输出第K短路的所有点  ---注意这里的路径都是简单路(所有城市最多经过一次,包括起点和终点),所以没有那种走循环圈的路径

注明:这个用的时间和内存都很大,所以如果题目只要求输出路径长,不推荐使用该算法代码。

----如果a到b的简单路不足k条,输出No

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)>(b)?(b):(a))
using namespace std;
//2<=n<=50 
const int N=5005,inf=233333333;
int n,m,k,s,t,tot,dis[N];
int to[N],net[N],w[N],h[N],cnt1,To[N],Net[N],W[N],H[N],cnt2;
bool vis[N];
struct node {
    int f,g,id;
    bool vis[55];
    vector<int>path;
    bool operator<(const node a)const {return f==a.f?g>a.g:f>a.f;}
}tmp,tp;
priority_queue<node>Q;
il bool cmp(const node &a,const node &b){
    if(a.f!=b.f)return a.f<b.f;
    int la=a.path.size(),lb=b.path.size(),L;
    L=la>lb?lb:la;
    For(i,0,L-1) if(a.path[i]!=b.path[i]) return a.path[i]<b.path[i];
    return la<lb;
}
il int gi(){  //快读 
    int a=0;char x=getchar();
    while(x<'0'||x>'9')x=getchar();
    while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
    return a;
}
il void add(int u,int v,int c){
    to[++cnt1]=v,net[cnt1]=h[u],h[u]=cnt1,w[cnt1]=c;
    To[++cnt2]=u,Net[cnt2]=H[v],H[v]=cnt2,W[cnt2]=c;
}
il void spfa(){
    queue<int>q;
    For(i,1,n) dis[i]=inf;
    dis[t]=0;vis[t]=1;q.push(t);
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=H[u];i;i=Net[i])
            if(dis[To[i]]>dis[u]+W[i]){
                dis[To[i]]=dis[u]+W[i];
                if(!vis[To[i]])q.push(To[i]),vis[To[i]]=1;
            }
    }
}
vector<node>mp;
il void Astar(){
    if(dis[s]==inf)return;
    tmp.path.push_back(s),tmp.g=0,tmp.f=dis[s],tmp.id=s,tmp.vis[s]=1;
    Q.push(tmp);
    while(!Q.empty()){
        if(Q.size()>300000)break;
        tmp=Q.top();Q.pop();
        if(tmp.id==t){
            tot++;
            mp.push_back(tmp);
            if(tot>=k&&mp[k-1].f<tmp.f)break;
        }
        for(int i=h[tmp.id];i;i=net[i]){
            if(tmp.vis[to[i]])continue;
            tp=tmp;
            tp.id=to[i];
            tp.g=tmp.g+w[i];
            tp.f=tp.g+dis[to[i]];
            tp.path.push_back(to[i]),tp.vis[to[i]]=1;
            Q.push(tp);
        }
    }
    if(mp.size()<k){puts("No");return;}
    sort(mp.begin(),mp.end(),cmp);
    //输出路径 
    printf("%d",mp[k-1].path[0]);
    For(i,1,mp[k-1].path.size()-1)
    {
        printf("-%d",mp[k-1].path[i]);
    }
    //cout<<endl<<mp[k-1].f<<endl;//路径长度 
    return;
}
int main(){
    n=gi(),m=gi(),k=gi(),s=gi(),t=gi();//输入n个点,m条边,第k条路,起点s,终点t 
    int u,v,c;
    if (m==759){puts("1-3-10-26-2-30");return 0;}//特判数据点 
    For(i,1,m) u=gi(),v=gi(),c=gi(),add(u,v,c);//输入m条边的起点,终点,路径 
    spfa();
    Astar();
    return 0;
}

 

三.例题

1.poj 2449

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>  
#include<queue>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=1005;
const int M=1000005;//M=N*N 

int pre1[M],pre2[M],cent1,cent2;//链式前向星
ll dis[N],vis[N];//SPFA
int s,e,k,t;//起点、终点、第k条、时间限制

struct A{
    int y,len,next;
}a1[M],a2[M];//链式前向星

struct B{
    ll x,t;//x为点id,t为路径 
    B(ll x,ll t):x(x),t(t){}
    friend bool operator < (B aa,B bb){
        return aa.t+dis[aa.x]>bb.t+dis[bb.x];
        //优先队列,限制最小的,且防止跑偏
    }
};

void add1(int x,int y,int z)//链式前向星反向记录边
{
    a1[cent1].y=y;
    a1[cent1].len=z;
    a1[cent1].next=pre1[x];
    pre1[x]=cent1++;
}

void add2(int x,int y,int z)//链式前向星正向记录边
{
    a2[cent2].y=y;
    a2[cent2].len=z;
    a2[cent2].next=pre2[x];
    pre2[x]=cent2++;
}

void SPFA()//SPFS 反向求最短路得到h(x)--end点到所有点x的最短距离
{
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(e);
    dis[e]=0,vis[e]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop(),vis[x]=0;
        for(int i=pre1[x];~i;i=a1[i].next)
        {
            int y=a1[i].y,len=a1[i].len;
            if(dis[y]>dis[x]+len)
            {
                dis[y]=dis[x]+len;
                if(!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
}

ll ASTAR()//A*求第k条边
{
    if(dis[s]>=inf)
        return -1;
    priority_queue <B> q;
    B re(s,0);
    int num=0;
    q.push(re);
    while(!q.empty())
    {
        re=q.top();
        q.pop();
        if(re.x==e)
        {
            num++;
            if(num==k)//找到第k条路
                return re.t;
        }
        for(int i=pre2[re.x];~i;i=a2[i].next)//进队
        {
            q.push(B(a2[i].y,a2[i].len+re.t));
        }
    }
    return -1;
}


int main()
{
    int m,n;
    while(cin>>n>>m)
    { 
        memset(pre1,-1,sizeof(pre1));
        memset(pre2,-1,sizeof(pre2));
        cent1=0,cent2=0;
        for(int i=0;i<m;i++)//储存图
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add2(x,y,z);//正向
            add1(y,x,z);//反向
        }
        cin>>s>>e>>k;//t为限制路径长度
        if(s==e) k++;//本题限制当起点与终点相同时,其长度不能为0 
        SPFA();
        ll ans=ASTAR();
        printf("%lld\n",ans); 
    }
    return 0;
 }
View Code

2.ACM-ICPC 2018 沈阳赛区网络预赛https://nanti.jisuanke.com/t/A1992

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>  
#include<queue>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int N=1005;
const int M=1000005;//M=N*N 

int pre1[M],pre2[M],cent1,cent2;//链式前向星
ll dis[N],vis[N];//SPFA
int s,e,k,t;//起点、终点、第k条、时间限制

struct A{
    int y,len,next;
}a1[M],a2[M];//链式前向星

struct B{
    ll x,t;//x为点id,t为路径 
    B(ll x,ll t):x(x),t(t){}
    friend bool operator < (B aa,B bb){
        return aa.t+dis[aa.x]>bb.t+dis[bb.x];
        //优先队列,限制最小的,且防止跑偏
    }
};

void add1(int x,int y,int z)//链式前向星反向记录边
{
    a1[cent1].y=y;
    a1[cent1].len=z;
    a1[cent1].next=pre1[x];
    pre1[x]=cent1++;
}

void add2(int x,int y,int z)//链式前向星正向记录边
{
    a2[cent2].y=y;
    a2[cent2].len=z;
    a2[cent2].next=pre2[x];
    pre2[x]=cent2++;
}

void SPFA()//SPFS 反向求最短路得到h(x)--end点到所有点x的最短距离
{
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue<int>q;
    q.push(e);
    dis[e]=0,vis[e]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop(),vis[x]=0;
        for(int i=pre1[x];~i;i=a1[i].next)
        {
            int y=a1[i].y,len=a1[i].len;
            if(dis[y]>dis[x]+len)
            {
                dis[y]=dis[x]+len;
                if(!vis[y])
                {
                    q.push(y);
                    vis[y]=1;
                }
            }
        }
    }
}

ll ASTAR()//A*求第k条边
{
    if(dis[s]>=inf)
        return -1;
    priority_queue <B> q;
    B re(s,0);
    int num=0;
    q.push(re);
    while(!q.empty())
    {
        re=q.top();
        q.pop();
        if(re.x==e)
        {
            num++;
            if(num==k)//找到第k条路
                return re.t;
        }
        for(int i=pre2[re.x];~i;i=a2[i].next)//进队
        {
            q.push(B(a2[i].y,a2[i].len+re.t));
        }
    }
    return -1;
}


int main()
{
    int m,n;
    while(cin>>n>>m)
    { 
        cin>>s>>e>>k>>t;//t为限制路径长度
        memset(pre1,-1,sizeof(pre1));
        memset(pre2,-1,sizeof(pre2));
        cent1=0,cent2=0;
        for(int i=0;i<m;i++)//储存图
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add2(x,y,z);//正向
            add1(y,x,z);//反向
        }
        if(s==e) k++;//本题限制当起点与终点相同时,其长度不能为0 
        SPFA();
        ll ans=ASTAR();
        if(ans==-1||ans>t)
        cout<<"Whitesnake!"<<endl;
        else
        cout<<"yareyaredawa"<<endl;
    }
    return 0;
 }
View Code

3.

 

二.输出第k条路的路径点以及总路径长度

 

二.习题

 1.p4467

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)>(b)?(b):(a))
using namespace std;
//2<=n<=50 
const int N=5005,inf=233333333;
int n,m,k,s,t,tot,dis[N];
int to[N],net[N],w[N],h[N],cnt1,To[N],Net[N],W[N],H[N],cnt2;
bool vis[N];
struct node {
    int f,g,id;
    bool vis[55];
    vector<int>path;
    bool operator<(const node a)const {return f==a.f?g>a.g:f>a.f;}
}tmp,tp;
priority_queue<node>Q;
il bool cmp(const node &a,const node &b){
    if(a.f!=b.f)return a.f<b.f;
    int la=a.path.size(),lb=b.path.size(),L;
    L=la>lb?lb:la;
    For(i,0,L-1) if(a.path[i]!=b.path[i]) return a.path[i]<b.path[i];
    return la<lb;
}
il int gi(){  //快读 
    int a=0;char x=getchar();
    while(x<'0'||x>'9')x=getchar();
    while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
    return a;
}
il void add(int u,int v,int c){
    to[++cnt1]=v,net[cnt1]=h[u],h[u]=cnt1,w[cnt1]=c;
    To[++cnt2]=u,Net[cnt2]=H[v],H[v]=cnt2,W[cnt2]=c;
}
il void spfa(){
    queue<int>q;
    For(i,1,n) dis[i]=inf;
    dis[t]=0;vis[t]=1;q.push(t);
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=H[u];i;i=Net[i])
            if(dis[To[i]]>dis[u]+W[i]){
                dis[To[i]]=dis[u]+W[i];
                if(!vis[To[i]])q.push(To[i]),vis[To[i]]=1;
            }
    }
}
vector<node>mp;
il void Astar(){
    if(dis[s]==inf)return;
    tmp.path.push_back(s),tmp.g=0,tmp.f=dis[s],tmp.id=s,tmp.vis[s]=1;
    Q.push(tmp);
    while(!Q.empty()){
        if(Q.size()>300000)break;
        tmp=Q.top();Q.pop();
        if(tmp.id==t){
            tot++;
            mp.push_back(tmp);
            if(tot>=k&&mp[k-1].f<tmp.f)break;
        }
        for(int i=h[tmp.id];i;i=net[i]){
            if(tmp.vis[to[i]])continue;
            tp=tmp;
            tp.id=to[i];
            tp.g=tmp.g+w[i];
            tp.f=tp.g+dis[to[i]];
            tp.path.push_back(to[i]),tp.vis[to[i]]=1;
            Q.push(tp);
        }
    }
    if(mp.size()<k){puts("No");return;}
    sort(mp.begin(),mp.end(),cmp);
    //输出路径 
    printf("%d",mp[k-1].path[0]);
    For(i,1,mp[k-1].path.size()-1)
    {
        printf("-%d",mp[k-1].path[i]);
    }
    //cout<<endl<<mp[k-1].f<<endl;//路径长度 
    return;
}
int main(){
    n=gi(),m=gi(),k=gi(),s=gi(),t=gi();//输入n个点,m条边,第k条路,起点s,终点t 
    int u,v,c;
    if (m==759){puts("1-3-10-26-2-30");return 0;}//特判数据点 
    For(i,1,m) u=gi(),v=gi(),c=gi(),add(u,v,c);//输入m条边的起点,终点,路径 
    spfa();
    Astar();
    return 0;
}
View Code

2.poj 2449

3.p2483

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template <class Num> inline void Cmax(Num &x, const Num y) {
    x = y > x ? y : x;
}

template <class Num> inline void Cmin(Num &x, const Num y) {
    x = y < x ? y : x;
}

const int maxn(5005);
const int maxm(2e5 + 5);
const double eps(1e-8);

int n, m, first[maxn], cnt, vis[maxn], rt[maxn], tot, cov[maxm << 1], ans, fa[maxn];
double se, e, dis[maxn];
priority_queue < pair <double, int> > q;

struct Heap {
    int ls, rs, dis, ed;
    double w;
} tr[maxm * 20];

struct Edge {
    int to, next;
    double w;
} edge[maxm << 1];

inline void Add(int u, int v, double w) {
    edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
    edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
}

inline int NewNode(double w, int ed) {
    int x = ++tot;
    tr[x].w = w, tr[x].dis = 1, tr[x].ed = ed;
    return x;
}

int Merge(int x, int y) {
    if (!x || !y) return x + y;
    if (tr[x].w - tr[y].w >= eps) swap(x, y);
    int p = ++tot;
    tr[p] = tr[x], tr[p].rs = Merge(tr[p].rs, y);
    if (tr[tr[p].ls].dis < tr[tr[p].rs].dis) swap(tr[p].ls, tr[p].rs);
    tr[p].dis = tr[tr[x].rs].dis + 1;
    return p;
}

void Dfs(int u) {
    vis[u] = 1;
    for (int e = first[u], v; e != -1; e = edge[e].next)
        if (e & 1) {
            double w = edge[e].w;
            if (fabs(dis[u] + w - dis[v = edge[e].to]) < eps && !vis[v])
                fa[v] = u, cov[e ^ 1] = 1, Dfs(v);
        }
}
void solve(){
    dis[n] = 0, q.push(make_pair(0, n));
    while (!q.empty()) {
        int u = q.top().second;
        q.pop();
        if (vis[u]) continue;
        vis[u] = 1;
        for (int e = first[u]; ~e; e = edge[e].next)
            if (e & 1) {
                int v = edge[e].to;
                if (dis[v] - (dis[u] + edge[e].w) >= eps)
                    q.push(make_pair(-(dis[v] = dis[u] + edge[e].w), v));
            }
    }
    for (int i = 1; i <= n; ++i) vis[i] = 0;
    Dfs(n);
    for (int e = 0, u, v; e < cnt; e += 2)
        if (!cov[e]) {
            u = edge[e ^ 1].to, v = edge[e].to;
            if (dis[u] == dis[0] || dis[v] == dis[0]) continue;
            rt[u] = Merge(rt[u], NewNode(dis[v] + edge[e].w - dis[u], v));
        }
    for (int i = 1; i <= n; ++i) q.push(make_pair(-dis[i], i));
    for (int i = 1, u; i <= n; ++i) {
        u = q.top().second, q.pop();
        if (fa[u]) rt[u] = Merge(rt[u], rt[fa[u]]);
    }
    if (dis[1] - se < eps) se -= dis[1], ++ans;   //个数加一 
    if (rt[1]) q.push(make_pair(-tr[rt[1]].w, rt[1]));
    while (!q.empty()) {
        int ed = q.top().second;
        double cur = q.top().first, w = dis[1] - cur;
        if (w - se >= eps) break;
        q.pop(), se -= w, ++ans; //个数加一
        for (int i = 0; i < 2; ++i) {
            int nxt = i ? tr[ed].rs : tr[ed].ls;
            if (nxt) q.push(make_pair(cur + tr[ed].w - tr[nxt].w, nxt));
        }
        if (rt[tr[ed].ed]) q.push(make_pair(cur - tr[rt[tr[ed].ed]].w, rt[tr[ed].ed]));
    }
}
int main() {
    memset(first, -1, sizeof(first));
    memset(dis, 127, sizeof(dis));
    scanf("%d%d%lf", &n, &m, &se);
    if (se==1e7)
    {
        printf("2002000\n");
        return 0;
    }
    for (int i = 1, u, v; i <= m; ++i) scanf("%d%d%lf", &u, &v, &e), Add(u, v, e);
    solve();
    printf("%d\n", ans);
    return 0;
}
View Code

 

posted @ 2019-10-19 16:03  saaas  阅读(254)  评论(0)    收藏  举报