求神佬解答有关重载运算符和make_pair的一些问题

P4779 【模板】单源最短路径(标准版)

Owen_codeisking @ 2018-07-25 10:29:40

为什么重载运算符改成make_pair就好了?没改的话

戳这

这是改进前的代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,s;
struct Edge
{
    ll to,v,next;
}e[500010];
ll tot,dis[500010],head[500010],vis[500010];
struct cmp
{
    bool operator()(ll a,ll b){
        return dis[a]>dis[b];
    }
};
priority_queue<ll,vector<ll>,cmp> pq;

void Link(ll x,ll y,ll w){
    e[++tot].to=y;
    e[tot].v=w;
    e[tot].next=head[x];
    head[x]=tot;
}

void Dijsktra(){
    ll u,v;
    pq.push(s);
    while(!pq.empty()){
        u=pq.top(),pq.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(ll i=head[u];i;i=e[i].next){
            v=e[i].to;
            if(dis[v]>dis[u]+e[i].v){
                dis[v]=dis[u]+e[i].v;
                pq.push(v);
            }
        }
    }
}

int main()
{
    /*freopen("testdata.in","r",stdin);
    freopen("testdata.out","w",stdout);*/
    scanf("%lld%lld%lld",&n,&m,&s);
    for(ll i=1;i<=m;i++){
        ll x,y,w;
        scanf("%lld%lld%lld",&x,&y,&w);
        Link(x,y,w);
    }   
    for(ll i=1;i<=n;i++)
        dis[i]=2147483647;
    dis[s]=0;
    Dijsktra();
    for(ll i=1;i<n;i++)
        printf("%lld ",dis[i]);
    printf("%lld\n",dis[n]);
    return 0;
}

这是改进后的代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,s;
struct Edge
{
    ll to,v,next;
}e[500010];
ll tot,dis[500010],head[500010],vis[500010];
priority_queue< pair<int,int> > pq;

void Link(ll x,ll y,ll w){
    e[++tot].to=y;
    e[tot].v=w;
    e[tot].next=head[x];
    head[x]=tot;
}

void Dijsktra(){
    ll u,v;
    pq.push(make_pair(0,s));
    while(!pq.empty()){
        u=pq.top().second;pq.pop();
        if(vis[u]) {
            continue;
        }
        vis[u]=1;
        for(ll i=head[u];i;i=e[i].next){
            v=e[i].to;
            if(dis[v]>dis[u]+e[i].v){
                dis[v]=dis[u]+e[i].v;
                pq.push(make_pair(-dis[v],v));
            }
        }
    }
}

int main()
{
    /*freopen("testdata.in","r",stdin);
    freopen("testdata.out","w",stdout);*/
    scanf("%lld%lld%lld",&n,&m,&s);
    for(ll i=1;i<=m;i++){
        ll x,y,w;
        scanf("%lld%lld%lld",&x,&y,&w);
        Link(x,y,w);
    }   
    for(ll i=1;i<=n;i++)
        dis[i]=1e10;
    dis[s]=0;
    Dijsktra();
    for(ll i=1;i<n;i++)
        printf("%lld ",dis[i]);
    printf("%lld\n",dis[n]);
    return 0;
}

这是题解

#include<bits/stdc++.h>
#define M(x,y) make_pair(x,y)
using namespace std;
int fr[100010],to[200010],nex[200010],v[200010],tl,d[100010];
bool b[100010];
void add(int x,int y,int w){
    to[++tl]=y;
    v[tl]=w;
    nex[tl]=fr[x];
    fr[x]=tl;
}
priority_queue< pair<int,int> > q;
int main(){
    int n,m,x,y,z,s;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    for(int i=1;i<=n;i++) d[i]=1e10;
    d[s]=0;
    q.push(M(0,s));
    while(!q.empty()){
        int x=q.top().second;
        q.pop(); 
        if(b[x]) continue;
        b[x]=1;
        for(int i=fr[x];i;i=nex[i]){
            int y=to[i],l=v[i];
            if(d[y]>d[x]+l){
                d[y]=d[x]+l;
                q.push(M(-d[y],y));//懒得重载运算符
            }
        }
    }
    for(int i=1;i<=n;i++) printf("%d ",d[i]);
    return 0;
}

by SuperJvRuo @ 2018-07-25 10:50:42

priority_queue的内部实现是二叉堆,第一份代码里,dis改变后无法保证堆的性质,很可能炸掉


by Owen_codeisking @ 2018-07-25 15:27:11

@ACdreamer 这么搞笑。。。


by Owen_codeisking @ 2018-07-25 15:28:30

辣鸡 operator


by GKxx @ 2018-07-25 16:11:23

不是operator的锅吧。本来就应该用pair吧


by GKxx @ 2018-07-25 16:18:16

operator跟make_pair本身没有任何关系。第一份代码是你写得有问题。我一般都是这样写

struct CmpType {
    bool operator()(const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) {
    return lhs.second > rhs.second;
    }
};

然后

std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >, CmpType> pq;

那么priority_queue就会按照pair的第二关键字构造最小堆(我习惯first是节点编号,second是对应的dist),从而你每次从pq中取出的都是dist值最小的pair。

题解不重载运算符是因为他把dist取了相反数,并且first是dist而second是结点编号,pair的默认小于运算符是first为第一关键字,second为第二关键字,那么它就会按照dist的相反数构造“最大堆”,也就是按照dist构造最小堆了,殊途同归。


by GKxx @ 2018-07-25 16:28:36

按照你第一份代码那样写,意味着队列里的元素的优先级是由外界的dist变化决定的,priority_queue就无法实时保证队列里的元素满足堆性质了。我只要在外面随便改一改dist的值,priority_queue就废了


|