分层图最短路初学

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

中文题不解释题意了

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

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

#include
#include
#include
#include
#include
#include
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 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
#include
#include
#include
#include
#include
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,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

你可能感兴趣的:(最短路,dp)