0
点赞
收藏
分享

微信扫一扫

洛谷 P1629 邮递员送信(dijkstra算法)

whiteMu 2022-03-26 阅读 58

提示:如果你进来是为了学习dikstra算法的,抱歉你走错片场了

           这是一个小白的解题心路分享

解题历程:

刚刚看到这题时,先用了Floyd算法,也就是基于动态规划用dis[u][v]表示城市u到城市v的最短时间,代码如下:

#include<iostream>
#include<cstring>
#define endl "\n"
using namespace std;
typedef long long ll;
constexpr int maxn = 1005;
constexpr ll inf = 0x3f3f3f3f3f3f3f3fL;
ll dis[maxn][maxn];
int main(){
    memset(dis,0x3f,sizeof(dis));
    int n,m,t;
    cin >> n >> m;
    for(int i = 1;i<=n;i++)
        dis[i][i] = 0;//自己到自己的距离为0
    for(int i = 1;i<=m;i++){
        int u,v;
        ll w;
        cin >> u >> v >> w;
        dis[u][v] = w;
    }
    for(int k = 1;k<=n;k++)
        for(int i = 1;i<=n;i++)
            for(int j = 1;j<=n;j++)
                dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j]);
    ll ans = 0;
    for(int i = 2;i<=n;i++)
        ans += dis[1][i] + dis[i][1];
    cout << ans << endl;
    return 0;
}

 但是没注意时间复杂度,没过qwq

 

 

T了能理解,咱就是说为什么前四个点会WA呢?

然后猜测输入会有重边,把dis[u][v] = w 改成 dis[u][v] = min(dis[u][v],w)交了一遍:

 果然输入卡重边(这里含泪建议各位写Floyd的时候要不要贪小便宜qwq)

当然对于这个题目来说,这个算法很明显不行,这时就得换算法了

由于最近刚刚学了dijktra,于是打算牵出来遛一遛(bushi

起点为1,终点为2->n,跑一遍就行,然后把所有的值加起来

但是起点为2->n,终点为1时怎么办呢?那就跑n-1遍dijkstra呗(小白思路,各位大佬轻点骂qwq

然后代码如下:

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define endl "\n"
using namespace std;
typedef long long ll;
constexpr int maxn = 2550;
constexpr ll inf = 0x3f3f3f3f3f3f3f3fL;

struct edge{
    int v;
    ll w;
    bool operator<(const edge& a)const{return w>a.w;}//定义排序方式
}e;
vector <edge> mp[maxn];
int dis[maxn];
void dij(int u){
    bool vis[maxn] = { false };//区别S点集和T点集,当vis[u] = true认为u在S点集中
    memset(dis,0x3f,sizeof(dis));//初始化dis
    dis[u] = 0;
    priority_queue<edge> T;//这里并不是存边,这里存的时当前状态下某一点和他的dis,偷懒下
    T.push({u,0});//源点及其距离推入队列
    while(!T.empty()){
        u = T.top().v;//取出队列中当前dis最小的值
        T.pop();
        if(vis[u])//如果已经访问过就跳过
            continue;
        vis[u] = true;//将其加入S集
        // for(auto&[v,w]:mp[u]){
        int len = mp[u].size();//松弛其所有出边,并将其终点加入队列
        for(int i = 0;i<len;i++){
            int v = mp[u][i].v;
            ll w = mp[u][i].w;
            if(dis[v]>dis[u]+w){
                dis[v] = dis[u] + w;
                T.push({v,dis[v]});
            }
            
        }
    }
}

int main(){
    int m,n,s,t;
    cin >> n >> m;
    for(int i = 0;i<m;i++){
        int v,u;
        ll w;
        cin >> u >> v >> w;
        e.v = v;
        e.w = w;
        mp[u].emplace_back(e);
        // e.v = u;
        // mp[v].emplace_back(e);
    }
    ll ans = 0;
    dij(1);
    for(int i = 2;i<=n;i++)
        ans += dis[i];
    for(int i = 2;i<=n;i++){
        dij(i);
        ans += dis[1];
    }
    cout << ans << endl;
    return 0;
}

然后又成功的T了

 然后突然我脑袋里冒出了一个大胆的想法:反向dijkstra建图!

既然正向的可以解决1到2->n的时间,反向的话是不是就能解决2->n到1了

然后相当于只用跑2遍dijkstra就行,有思路就好搞了对吧

AC代码如下:

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define endl "\n"
using namespace std;
typedef long long ll;
constexpr int maxn = 1005;
constexpr ll inf = 0x3f3f3f3f3f3f3f3fL;
struct edge{
    int v;
    ll w;
    bool operator<(const edge& a)const{return w>a.w;}//定义排序方式
}e;
struct edge2{
    int u;
    ll w;
    bool operator<(const edge& a)const{return w>a.w;}
}e2;//但是后来想了下好像这里没必要再定义个结构体,读者有兴趣可以试试减少代码量
vector <edge> mp[maxn];
vector <edge2> mp2[maxn];
int dis[maxn] = { 0 };
void dij(int u){
    bool vis[maxn] = { false };//区别S点集和T点集,当vis[u] = true认为u在S点集中
    memset(dis,0x3f,sizeof(dis));//初始化dis
    dis[u] = 0;
    priority_queue<edge> T;//这里并不是存边,这里存的时当前状态下某一点和他的dis
    T.push({u,0});//源点及其距离推入队列
    while(!T.empty()){
        u = T.top().v;//取出队列中当前dis最小的值
        T.pop();
        if(vis[u])//如果已经访问过就跳过
            continue;
        vis[u] = true;//将其加入S集
        // for(auto&[v,w]:mp[u]){
        int len = mp[u].size();//松弛其所有出边,并将其终点加入队列
        for(int i = 0;i<len;i++){
            int v = mp[u][i].v;
            ll w = mp[u][i].w;
            if(dis[v]>dis[u]+w){
                dis[v] = dis[u] + w;
                T.push({v,dis[v]});
            }
            
        }
    }
}
void dij2(int v){
    bool vis[maxn] = { false };//咱就是说复制下上面的就行了对吧,u,v互换下
    memset(dis,0x3f,sizeof(dis));
    dis[v] = 0;
    priority_queue<edge> T;
    T.push({v,0});
    while(!T.empty()){
        v = T.top().v;
        T.pop();
        if(vis[v])
            continue;
        vis[v] = true;
        int len = mp2[v].size();
        for(int i = 0;i<len;i++){
            int u = mp2[v][i].u;
            ll w = mp2[v][i].w;
            if(dis[u]>dis[v]+w){
                dis[u] = dis[v] + w;
                T.push({u,dis[u]});
            }
        }
    }
}
int main(){
    int m,n,s,t;
    cin >> n >> m;
    for(int i = 0;i<m;i++){
        int v,u;
        ll w;
        cin >> u >> v >> w;
        e.v = v;
        e2.u = u;
        e.w = w;
        e2.w = w;
        mp[u].emplace_back(e);
        mp2[v].emplace_back(e2);
    }
    ll ans = 0;
    dij(1);
    for(int i = 2;i<=n;i++)
        ans += dis[i];
    dij2(1);
    for(int i = 2;i<=n;i++){
        ans += dis[i];
    }
    cout << ans << endl;
    return 0;
}

可能看着臭长了些,但能AC的代码都是好代码对吧【doge

好耶!!!

说明,写本文前搜了下发现已经有很多人用这个思路过了,但想了下我还是写了出来

因为不管怎么说这也是我这个小菜狗独立完成的不是【doge

好激动,就当是一次分享心情吧 ,各位看官笑笑就行,不用赞之类的

希望以后每次解决题目也会像今天这般欣喜

好了废话不多说,如有不对之处还请各位雅正(轻点骂,我怕疼bushi

祝各位天天AC好心情

举报

相关推荐

0 条评论