分层图最短路初学

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2763

中文题不解释题意了

思路:我们设置dis[i][k]表示走到第i号点,免费经过了k条边的最短路。
对于我们当前找到的终点,尝试起点的状态去更新,不选择此条边免费的状态和选择此条边免费的状态,再将这两个状态压入队列去更新可以到达的其他状态。

代码(原oj好像不能注册就没有再注册去提交,样例可以过就是了)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define LL long long
const int MOD=1e9+7;
const int inf=0x3f3f3f3f;
const LL MAX_N=10005;
const LL MAX_M=100005;
#define MEF(x) memset(x,-1,sizeof(x))
#define ME0(x) memset(x,0,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
struct D
{
    LL i,k;
};
struct LX
{
    LL st,ed,di,next;
}lx[MAX_M];
LL first[MAX_N];
LL dis[MAX_N][15];
LL vis[MAX_N][15];
LL cnt;
void add(LL s,LL e,LL d)
{
    lx[++cnt].st=s;
    lx[cnt].ed=e;
    lx[cnt].di=d;
    lx[cnt].next=first[s];
    first[s]=cnt;
}
void spfa(LL st,LL ed,LL k)
{
    queue<D> q;
    D d;
    MEI(dis);
    ME0(vis);
    d.i=st;d.k=0;
    dis[st][0]=0;
    q.push(d);
    vis[st][0]=1;
    while(!q.empty())
    {
        D d=q.front();
        LL x=d.i,kk=d.k;
        q.pop();
        vis[x][kk]=0;
        for(LL i=first[x];i!=-1;i=lx[i].next)
        {
            LL y=lx[i].ed;
            if(dis[y][kk]>dis[x][kk]+lx[i].di)
            {
                dis[y][kk]=dis[x][kk]+lx[i].di;
                if(!vis[y][kk])
                {
                    D dd;
                    dd.i=y;dd.k=kk;
                    q.push(dd);
                    vis[y][kk]=1;
                }
            }
            if(kk+1<=k)
            {
                if(dis[y][kk+1]>dis[x][kk])
                {
                    dis[y][kk+1]=dis[x][kk];
                    if(!vis[y][kk+1])
                    {
                        D dd;
                        dd.i=y;dd.k=kk+1;
                        q.push(dd);
                        vis[y][kk+1]=1;
                    }
                }
            }
        }
    }
    printf("%lld\n",dis[ed][k]);
}
int main()
{
    LL n,m,k,st,ed,s,e,d;
    scanf("%lld%lld%lld%lld%lld",&n,&m,&k,&st,&ed);
    cnt=0;
    MEF(first);
    for(int m1=1;m1<=m;m1++)
    {
        scanf("%lld%lld%lld",&s,&e,&d);
        add(s,e,d);
        add(e,s,d);
    }
    spfa(st,ed,k);
    return 0;
}

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

题意:在一个n个点m条边的图中求1号点到n号店的最短距离,你有k次机会使某条路的权值变成0

输入:

第一行一个T,表示T个样例

接下来一行输入n,m,k(n,m,k意思如题意)

接下来m行u,v,c(u表示起点,v表示终点,c表示距离)单向边

思路:优先队列优化的dijstra,不同的是改变了dis数组的意义,我们设置dis[i][k]表示走到第i号点,免费经过了k条边的最短路

个人感觉用到了dp的思想,每一次更新状态时都更新两次,一次是选择当前边不变成0,另一个是选择当前边变成0,把更新的状态推进队列用来更新其他状态。

AC代码:(spfa好像会被卡时间,出题人的锅。。。。。。)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
using namespace std;
#define LL long long
const int MOD=1e9+7;
const int inf=0x3f3f3f3f;
const LL MAX_N=100005;
const LL MAX_M=200005;
#define MEF(x) memset(x,-1,sizeof(x))
#define ME0(x) memset(x,0,sizeof(x))
#define MEI(x) memset(x,inf,sizeof(x))
struct D
{
    LL i,k,diss;
};
struct LX
{
    LL st,ed,di,next;
}lx[MAX_M];
LL first[MAX_N];
LL dis[MAX_N][15];
LL vis[MAX_N][15];
LL cnt;
void add(LL s,LL e,LL d)
{
    lx[++cnt].st=s;
    lx[cnt].ed=e;
    lx[cnt].di=d;
    lx[cnt].next=first[s];
    first[s]=cnt;
}
struct cmp
{
    bool operator()(const D a,const D b)
    {
        return a.diss>b.diss;
    }
};
void dijstra(LL st,LL ed,LL k)
{
    priority_queue<D,vector<D>,cmp> q;
    MEI(dis);
    ME0(vis);
    D d;
    d.i=st;d.k=0;d.diss=0;
    dis[st][0]=0;
    q.push(d);
    while(!q.empty())
    {
        D dd=q.top();
        q.pop();
        LL x=dd.i,kk=dd.k;
        if(vis[x][kk])
        {
            continue;
        }
        vis[x][kk]=1;
        for(LL i=first[x];i!=-1;i=lx[i].next)
        {
            int y=lx[i].ed;
            if(!vis[y][kk]&&dis[y][kk]>dis[x][kk]+lx[i].di)
            {
                dis[y][kk]=dis[x][kk]+lx[i].di;
                D ddd;
                ddd.i=y;ddd.k=kk;ddd.diss=dis[y][kk];
                q.push(ddd);
            }
            if(kk+1<=k&&!vis[y][kk+1]&&dis[y][kk+1]>dis[x][kk])
            {
                dis[y][kk+1]=dis[x][kk];
                D ddd;
                ddd.i=y;ddd.k=kk+1;ddd.diss=dis[y][kk+1];
                q.push(ddd);
            }
        }
    }
    printf("%lld\n",dis[ed][k]);
}
int main()
{
    LL t;
    scanf("%lld",&t);
    for(LL t1=1;t1<=t;t1++)
    {
        LL n,m,k,s,e,d;
        scanf("%lld%lld%lld",&n,&m,&k);
        cnt=0;
        MEF(first);
        for(int m1=1;m1<=m;m1++)
        {
            scanf("%lld%lld%lld",&s,&e,&d);
            add(s,e,d);
        }
        dijstra(1,n,k);
    }
    return 0;
}

参考博客:https://blog.csdn.net/qq_36693533/article/details/78466623

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值