洛谷P3556 [POI2013] MOR-Tales of seafaring的三种解法

本题模板为奇偶最短路(边权为1时的),题目链接:https://www.luogu.com.cn/problem/P3556

为了研究,码了三种不同最短路解放的奇偶做法,便于不同群体理解.

一:BFS,对于边权为1,求最短路当然是BFS最快了,时间复杂度:o(nm),代码如下:

点击查看代码
//背景:我的BFS奇偶最短路尝试
//思路:对于边权为1的情况,毫无疑问,这是我们最开始学BFS时的应用,对于单源最短路边权为1的最优解就是BFS,同时可以证明边权为1时SPFA算法也取最优时间
//时间复杂度:o(nm)
#include <bits/stdc++.h>
using namespace std;
typedef struct situation
{
    int to,d,id;
}SI;
vector<int> e[5001];
vector<SI> ask[5001];
bool ans[1000001];
int dis[5001][2];
void bfs(int b)//主要看这里
{
    bool vis[5001][2];//奇偶标记数组
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    vis[b][0] = true;
    dis[b][0] = 0;//奇偶答案
    queue<pair<int,int>> que;
    que.push({b,0});
    while(!que.empty())
    {
        int x = que.front().first;
        int y = que.front().second;
        que.pop();
        for (auto v:e[x])//漂亮的奇偶处理
        {
            if(vis[v][y^1]) continue;
            dis[v][y^1] = dis[x][y] + 1;
            vis[v][y^1] = true;
            que.push({v,y^1});
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    memset(dis,0x3f,sizeof(dis));
    const int INF = dis[0][0];
    int n,m,q,x,y,d;
    cin>>n>>m>>q;
    for (int i = 1;i<=m;i++)
    {
        cin>>x>>y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    for (int i = 1;i<=q;i++)
    {
        cin>>x>>y>>d;
        ask[x].push_back({y,d,i});
    }
    for (int i = 1;i<=n;i++)
    {
        if(ask[i].empty()) continue;
        bfs(i);
        for (auto v:ask[i])
        {
            x = v.to;
            y = v.d;
            int temp = v.id;
            if(e[i].size() == 0) ans[temp] = false;
            else
            {
                if(dis[x][y%2] == INF) ans[temp] = false;
                else
                {
                    if(dis[x][y%2] <= y) ans[temp] = true;
                    else ans[temp] = false;
                }
            }
        }
    }
    for (int i = 1;i<=q;i++)
    {
        if(ans[i]) cout<<"TAK"<<"\n";
        else cout<<"NIE"<<"\n";
    }
    return 0;
}

二:SPFA算法,不稳定算法,时间复杂度优则o(m),坏则o(nm),可以证明对于边权为1的情况,SPFA等价于BFS,故时间复杂度为o(nm),代码如下:

点击查看代码
//背景:超级恶心的题目,涉及奇偶最短路,且时间复杂度感觉过不了!且细节超级多
//收获:SPFA求奇偶最短路,简单路是没有重复节点的路,题目求的不是简单路,所以应该要奇偶处理,而我发现SPFA的奇偶处理比Dijkstra好处理
//原理:最短路 + 离线处理
//时间复杂度:最优o(nm),最坏(n^2*m),一般来说跑的比较快(听说的)
#include <bits/stdc++.h>
using namespace std;
typedef struct situation//离线处理结构体
{
    int to,id,d;//to为另一端点,id为询问的编号,d为要求的长度
}SI;
int n,m,k;
int dis[5001][2];//如果不离线处理的话,开成全源最短路数组会MLE!这是为什么要离线处理的原因
vector<int> e[5001];//邻接表存图
void SPFA(int now)//跑奇偶SPFA算法
{
    memset(dis,0x3f,sizeof(dis));
    bool vis[n+1];
    memset(vis,0,sizeof(vis));
    dis[now][0] = 0;
    queue<int> que;
    que.push(now);
    vis[now] = true;
    while(!que.empty())
    {
        int x = que.front();
        que.pop();
        vis[x] = false;
        for (auto v:e[x])
        {
            if(dis[v][1] > dis[x][0] + 1)//奇偶判断
            {
                dis[v][1] = dis[x][0] + 1;
                if(vis[v] == false)
                {
                    vis[v] = true;
                    que.push(v);
                }
            }
            if(dis[v][0] > dis[x][1] + 1)//奇偶判断
            {
                dis[v][0] = dis[x][1] + 1;
                if(vis[v] == false)
                {
                    vis[v] = true;
                    que.push(v);
                }
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    memset(dis,0x3f,sizeof(dis));
    const int INF = dis[0][0];//获取无穷大
    int x,y,d;
    cin>>n>>m>>k;
    vector<SI> ask[n+1];//离线询问数组
    for (int i = 1;i<=m;i++)
    {
        cin>>x>>y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    bool ans[k+1];//记录对应询问编号的答案,注意这里数组大小是k+1
    for (int i = 1;i<=k;i++)//离线处理
    {
        cin>>x>>y>>d;
        ask[x].push_back({y,i,d});
    }
    for (int i = 1;i<=n;i++)//依次以每个点为源点进行最短路处理
    {
        if(ask[i].empty()) continue;//如果对应它没有询问,没必要跑最短路,跳过
        SPFA(i);//跑SPFA最短路
        for (auto v:ask[i])//遍历询问
        {
            if(e[i].size() == 0)//!!!!重中之重,如果是孤立点,一定无解,可能有疑惑,孤立点不就是没有路了吗?我下面的判INF操作明明可以处理啊
							    //事实上,对于询问自己到自己的最短路时(对于孤立点的情况)可以Hack掉我的判断,所以这个还是要有的
            {
                ans[v.id] = false;//记录
                continue;//跳过
            }
            if(v.d%2 == 0)//如果是偶数
            {
                if(dis[v.to][0] == INF) ans[v.id] = false;//无路情况
                else
                {
                    if(dis[v.to][0] > v.d) ans[v.id] = false;//最短路大于询问,肯定不行
                    else ans[v.id] = true;
                }
            }
            else//奇数同理
            {
                if(dis[v.to][1] == INF) ans[v.id] = false;
                else
                {
                    if(dis[v.to][1] > v.d) ans[v.id] = false;
                    else ans[v.id] = true;
                }
            }
        }
    }
    for (int i = 1;i<=k;i++)//遍历询问
    {
        if(ans[i]) cout<<"TAK"<<"\n";//输出
        else cout<<"NIE"<<"\n";
    }
    return 0;
}




//下面是漂亮版的奇偶dp
//细节:vis[][]开二维
void SPFA(int now)//跑奇偶SPFA算法
{
    memset(dis,0x3f,sizeof(dis));
    bool vis[n+1][2];
    memset(vis,0,sizeof(vis));
    dis[now][0] = 0;
    queue<pair<int,int>> que;
    que.push({now,0});
    vis[now][0] = true;
    while(!que.empty())
    {
        int x = que.front().first;
        int y = que.front().second;
        que.pop();
        vis[x][y] = false;
        for (auto v:e[x])
        {
            if(dis[v][y^1] > dis[x][y] + 1)
            {
                dis[v][y^1] = dis[x][y] + 1;
                if(vis[v][y^1]) continue;
                vis[v][y^1] = true;
                que.push({v,y^1});
            }
        }
    }
}

三,Dijkstra算法,稳定算法,时间复杂度为o(nmlogm),代码:

点击查看代码
//背景:当Dijkstra算法求奇偶最短路归位时,终于我的全部方法求奇偶最短路均已实现
//分析:对于边权为1的情况,显然是SPFA与BFS快,但是边权不为1,Dijkstra无敌,但是边权不为1的代码估计会难写
//时间复杂度:o(nmlogm)
#include <bits/stdc++.h>
using namespace std;
typedef struct situation
{
    int to,d,id;
}SI;
typedef struct dij
{
    int u,d,odd;//在原Dijkstra元素结构体的基础上加了奇偶判断
}DIJ;
struct cmp
{
    bool operator()(DIJ x,DIJ y)
    {
        return x.d > y.d;
    }
};
vector<int> e[5001];
vector<SI> ask[5001];
bool ans[1000001];
int dis[5001][2];
void Dijkstra(int b)//主要差异
{
    bool vis[5001][2];
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));//这个初始化不要忘了
    dis[b][0] = 0;//初始化
    priority_queue<DIJ,vector<DIJ>,cmp> que;
    que.push({b,0,0});//入队
    while(!que.empty())
    {
        int x = que.top().u;
        int odd = que.top().odd;
        que.pop();//不要忘
        if(vis[x][odd]) continue;
        vis[x][odd] = true;
        for (auto v:e[x])
        {
            if(dis[v][odd^1] > dis[x][odd] + 1)
            {
                dis[v][odd^1] = dis[x][odd] + 1;
                que.push({v,dis[v][odd^1],odd^1});
            }
        }
    }
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    memset(dis,0x3f,sizeof(dis));
    const int INF = dis[0][0];
    int n,m,q,x,y,d;
    cin>>n>>m>>q;
    for (int i = 1;i<=m;i++)
    {
        cin>>x>>y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    for (int i = 1;i<=q;i++)
    {
        cin>>x>>y>>d;
        ask[x].push_back({y,d,i});
    }
    for (int i = 1;i<=n;i++)
    {
        if(ask[i].empty()) continue;
        Dijkstra(i);
        for (auto v:ask[i])
        {
            x = v.to;
            y = v.d;
            int temp = v.id;
            if(e[i].size() == 0) ans[temp] = false;
            else
            {
                if(dis[x][y%2] == INF) ans[temp] = false;
                else
                {
                    if(dis[x][y%2] <= y) ans[temp] = true;
                    else ans[temp] = false;
                }
            }
        }
    }
    for (int i = 1;i<=q;i++)
    {
        if(ans[i]) cout<<"TAK"<<"\n";
        else cout<<"NIE"<<"\n";
    }
    return 0;
}

总结:对于边权为1,优先BFS或者SPFA,否则Dijkstra

码字不易,多多支持

posted @ 2024-05-13 20:42  游辰  阅读(28)  评论(0编辑  收藏  举报