ACM-ICPC 2018 南京赛区网络预赛 L题(分层最短路)

题目链接https://nanti.jisuanke.com/t/31001

题目大意:给出一个含有n个点m条边的带权有向图,求1号顶点到n号顶点的最短路,可以使<=k条任意边的权值变为0。

样例输入

1
5 6 1
1 2 2
1 3 4
2 4 3
3 4 1
3 5 6
4 5 2

样例输出

3


解题思路:可以用两种做法,不过都差不多,应该算是同一种思路的不同写法。
第一种是在建图时,将一个点拆成k个层次的点,应该总共有k+1层,每个相同层次的点按输入的边权连接,每个点可以向它能连接到的点的下一个层次连接一条边权为0的边,这样你每使一条边权值变为0,即相当于走到了下一层图,永远不能走回,当走到第k+1层图时即不能使用了,在这个含有k+1层图的大图里跑下最短路就可以得出答案了
附上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=2200007;
const ll inf=0x3f3f3f3f;
struct qnode{
    int u;
    ll dis;
    bool operator<(const qnode &a)const{
        return dis>a.dis;
    }
    qnode(int a,ll b)
    {
        u=a;
        dis=b;
    }
};
struct node{
    int v,w,next;
}edge[2*maxn];
int n,m,k,tot=0,head[maxn];
ll dis[maxn];
void add(int u,int v,int w)
{
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
void dij()
{
    priority_queue<qnode> que;
    memset(dis,inf,sizeof(dis));
    dis[1]=0;
    que.push(qnode(1,0));
    while(!que.empty())
    {
        int u=que.top().u;
        que.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v,w=edge[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                que.push(qnode(v,dis[v]));
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d%d%d",&n,&m,&k);
        for(int i=0;i<m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            for(int j=0;j<=k;j++)
            {
                add(u+j*n,v+j*n,w); //同一层次的点用输入边权相连
                if(j!=k)
                    add(u+j*n,v+(j+1)*n,0); //不同层次的点用0权值相连
            }
        }
        if(k>=m)
        {
            printf("0\n");
            continue;
        }
        ll ans=inf;
        dij();
        for(int i=0;i<=k;i++)
            ans=min(ans,dis[n+i*n]);
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

第二种是用最短路+dp思想,再开一维数组记录已经使用了几次使边的权值为0,也是跑下最短路就可以了。

附上代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
const long long inf=0x3f3f3f3f;
struct node{
    int v,w,next;
}edge[2*maxn];
struct qnode{
    int u,k;
    long long dis;
    bool operator<(const qnode &a)const{
        return dis>a.dis;
    }
    qnode(int a,int b,long long c)
    {
        u=a;
        k=b;
        dis=c;
    }
};
int n,m,k,tot=0,head[maxn];
long long dis[maxn][15];
void add(int u,int v,int w)
{
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
}
long long dijkstra()
{
    priority_queue<qnode> que;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=k;j++)
        {
            dis[i][j]=inf;
        }
    }
    que.push(qnode(1,0,0));
    dis[1][0]=0;
    while(!que.empty())
    {
        int u=que.top().u,tempk=que.top().k;
        if(u==n)
            return dis[u][tempk];
        que.pop();
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v,w=edge[i].w;
            if(dis[u][tempk]+w<dis[v][tempk])  //同一层的最短路
            {
                dis[v][tempk]=dis[u][tempk]+w;
                que.push(qnode(v,tempk,dis[v][tempk]));
            }
            if(tempk<k)
            {
                if(dis[u][tempk]<dis[v][tempk+1])  //如果将这条边权值变为0,就会进入tempk+1层
                {
                    dis[v][tempk+1]=dis[u][tempk];
                    que.push(qnode(v,tempk+1,dis[v][tempk+1]));
                }
            }
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        init();
        for(int i=0;i<m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        printf("%lld\n",dijkstra());
    }
    return 0;
}
View Code

 

posted @ 2018-10-05 11:11  两点够吗  阅读(183)  评论(0编辑  收藏  举报