首页 > 其他分享 >洛谷P3556 [POI2013] MOR-Tales of seafaring的三种解法

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

时间:2024-05-13 20:40:54浏览次数:11  
标签:奇偶 洛谷 int POI2013 Tales vis que push dis

本题模板为奇偶最短路(边权为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

码字不易,多多支持

标签:奇偶,洛谷,int,POI2013,Tales,vis,que,push,dis
From: https://www.cnblogs.com/cuijunjie18/p/18189966

相关文章

  • 洛谷题单指南-动态规划3-P1140 相似基因
    原题链接:https://www.luogu.com.cn/problem/P1140题意解读:两个只包含A、C、G、T4个字符的序列,根据已经定义好的字符-字符的相似度,计算两个序列最大的相似度,两个序列必须每个字符都配对,如果字符不够,可以插入'-'代替。解题思路:本题要解决几个问题:1、状态表示既然有两个序列,设......
  • 洛谷题单指南-动态规划3-P1880 [NOI1995] 石子合并
    原题链接:https://www.luogu.com.cn/problem/P1880题意解读:计算n堆石子合并的最小、最大得分,只不过这n堆石子是环形的,也就是首、尾也相邻,是区间DP的升级版-环形DP问题。解题思路:如果是常规区间DP的方法:对于n堆石子,考察区间的长度范围是1~n先枚举左端点i,范围是1~n再计算右......
  • 洛谷题单指南-动态规划3-P3205 [HNOI2010] 合唱队
    原题链接:https://www.luogu.com.cn/problem/P3205题意解读:给定理想队形,计算初始队形的方案数。解题思路:对于给定理想队形,最后一个人插入有两种可能:从左边插入、从右边插入从左边插入,则意味着前一个数比当前数大,前一个数有可能在左边也有可能在右边从右边插入,则意味着前一个数......
  • 洛谷 P2824 [HEOI2016/TJOI2016] 排序(二分,线段树)
    传送门解题思路据说是经典思路:把多次排序转化成二分+01序列。首先二分所求位置的数字是啥,将大于mid的数字变成1,将小于等于mid的数字变成0。这样在排序的时候就相当于统计区间里的1的个数(区间和),然后区间全部变成0或者1。也就是区间修改,区间求和,线段树可以实现。AC代码#inclu......
  • 洛谷题单指南-动态规划3-Zuma
    原题链接:https://www.luogu.com.cn/problem/CF607B题意解读:从一组整数中取连续的回文子串,求最少几次可以取完。解题思路:状态表示:设dp[i][j]表示取i~j之间的回文子串所需的最少次数,a[i]表示第i个数状态转移:如果a[i]==a[j],dp[i][j]=dp[i+1][j-1],因为首尾如果相等,其必然可以......
  • 5.10洛谷收获
    1.求幂函数#includepow(a,b);计算a的b次幂2.error:invalidtypes'int[int]'forarraysubscript|记住这个错误吧,犯过好多次了数组变量名不一致或者是没定义数组空间不够变量名和数组名重复定义3.快速幂快速幂本质上是一个倍增问题,比如说要求6的34次方如果34个6相乘......
  • #Trie#洛谷 6018 [Ynoi2010] Fusion tree
    题目给定一棵树,树上每个节点都有点权,需要实现三种操作,第一种是将与\(x\)相邻的所有节点点权加一,第二种是单点减小点权,第三种是查询与\(x\)相邻的所有节点点权的异或和分析相邻实际上就是父节点和子节点,不妨将其拆开考虑,需要解决单点查询单点修改的问题,考虑维护\(n\)......
  • 洛谷题单指南-动态规划3-P1775 石子合并(弱化版)
    原题链接:https://www.luogu.com.cn/problem/P1775题意解读:计算合并石子的最小代价,区间DP。解题思路:状态表示:dp[i][j]表示将第i~j堆石子合并的最小代价,m[i]表示第i堆石子质量,s[i]表示前i堆石子质量前缀和状态转移:考虑最后一次合并,设最后一次合并是将i~k合成的一堆与k+1~j合成......
  • 洛谷题单指南-动态规划2-P3147 [USACO16OPEN] 262144 P
    原题链接:https://www.luogu.com.cn/problem/P3147题意解读:将一组数据两两相邻且相同的合并,合并成一个数值+1的数,求合并后的最大值。解题思路:考虑合并后的最大数i,其最后一次必然是由两个i-1合并而来的设dp[i][j]表示以j为左端点,合并最大值为i时的右端点的下一个位置如图:dp[i......
  • 5.9洛谷收获
    今天发现了一个有用的容器,那就是向量,用bool类型的向量简直不要太方便,尤其是对于二极管问题,比如B2094然后用向量模拟栈也比较方便点击查看代码#include<iostream>#include<vector>usingnamespacestd;classStack{private:vector<int>elements;public://......