prim优先队列优化求助

P3366 【模板】最小生成树

NirvanaCeleste @ 2024-06-10 17:20:05

prim (堆)

#include <bits/stdc++.h>
using namespace std;
int n,m,tot,ans;
bool vis[1000100];
struct op{
    int vlu,to;
    op(int a,int b){vlu=a,to=b;}
    friend bool operator < (const op aa,const op bb){//重载小于号  注意不能重载大于号
        if(aa.vlu != bb.vlu) return aa.vlu < bb.vlu;
    }
};
struct node {
    int to,vlu;
} pll;
vector<node> adj[1000100];
priority_queue <op> stk;
void insert_edge(int u,int v,int w) {
    pll.to = v;
    pll.vlu = w;
    adj[u].push_back(pll);
    pll.to = u;
    adj[v].push_back(pll);
    pll.to = pll.vlu = 0;
}
void put_stk(int x) {
    vis[x] = 1;
    for(int i=0; i<adj[x].size(); ++i) {
        int v = adj[x][i].to,w = adj[x][i].vlu;
        if(!vis[v]) stk.push(op(w,v));
    }
}
int pop_stk() {
    int pop_temp = 0;
    while(vis[stk.top().to] && !stk.empty()) stk.pop();
    if(!stk.empty()){
        pop_temp = stk.top().vlu;
        put_stk(stk.top().to);
        stk.pop();
    }
    return pop_temp;
}
void prim(int x) {
    memset(vis,0,sizeof(vis));
    vis[x] = 1;
    put_stk(x);
    while(!stk.empty()) ans += pop_stk();
}
void dfs(int x) {
    vis[x] = 1;
    for(int i=0; i<adj[x].size(); ++i) {
        int v = adj[x][i].to;
        if(!vis[v]) dfs(v);
    }
    return;
}
bool pd() {
    dfs(1);
    for(int i=1; i<=n; i++) if(!vis[i]) return 0;
    return 1;
}
int main() {
    cin>>n>>m;
    int u,v,w;
    for(int i=1; i<=m; i++) {
        scanf("%d %d %d",&u,&v,&w);
        insert_edge(u,v,w);
    }
    if(m >= n - 1 && pd()) {
        prim(1);
        cout<<ans;
    } else cout<<"orz";
    return 0;
}

kru (AC)

#include <bits/stdc++.h>
using namespace std;
int f[1000100];
int n,m,tot,ans;
bool vis[1000100];
struct edge{
    int pre,to,w; 
}e[1000100];
vector<int> adj[1000100];
bool cmp(edge px,edge py){return px.w < py.w;}
void insert_edge(int u,int v,int w){
    e[++tot].pre = u;
    e[tot].to = v;
    e[tot].w = w;
    adj[u].push_back(v);
    adj[v].push_back(u);
}
int find(int x){
    if(f[x] == x) return x;
    else return f[x] = find(f[x]);
}
void kpp(){
    int fa = 0,fb = 0;
    for(int i=1;i<=tot;i++){
        fa = find(e[i].pre),fb = find(e[i].to);
        if(fa != fb) f[fa] = fb,ans += e[i].w;
    }
}
void dfs(int x){
    vis[x] = 1;
    for(int i=0;i<adj[x].size(); ++i){
        int v = adj[x][i];
        if(!vis[v]) dfs(v);
    }
    return;
}
bool pd(){
    dfs(1); 
    for(int i=1;i<=n;i++) if(!vis[i]) return 0;
    return 1;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) f[i] = i;
    int u,v,w;
    for(int i=1;i<=m;i++){
        scanf("%d %d %d",&u,&v,&w);
        insert_edge(u,v,w);
    }   
    if(m >= n - 1 && pd()){
        sort(e+1,e+tot+1,cmp);
        kpp();
        cout<<ans;
    }
    else cout<<"orz";
    return 0;
} 

by NirvanaCeleste @ 2024-06-12 11:56:49

#include <bits/stdc++.h>
using namespace std;
const int M = 1000100;
int n,m,ans,cnt;
bool vis[M];
struct op {
    int op_vlu,op_to;
    op(int a,int b) {
        op_vlu = a , op_to = b;
    }
    friend bool operator < (const op &aa,const op &bb) {
        return aa.op_vlu > bb.op_vlu;
    }
};
struct node {
    int to,vlu;
};
vector<node> adj[M];
priority_queue <op> stk;
void insert_edge(int u,int v,int w) {
    node pll;
    pll.vlu = w;
    pll.to = v;
    adj[u].push_back(pll);
}
void put_stk(int x) {
    cnt++;//
    vis[x] = 1;
    for(int i=0; i<adj[x].size(); ++i) {
        int xv = adj[x][i].to,xw = adj[x][i].vlu;
        if(!vis[xv]) stk.push(op(xw,xv));
    }
}
int pop_stk() {
    int pop_temp = 0,push_temp = 0;
    while(vis[stk.top().op_to] && !stk.empty()) stk.pop();
    if(!stk.empty()) {
        pop_temp = stk.top().op_vlu;
        push_temp = stk.top().op_to;
        stk.pop();
        put_stk(push_temp);
//      cnt++;// 为什么不能在这cnt++,因为第一个点加不上去,在这写需要让cnt=1 
//      put_stk(stk.top().op_to); 不能这样写,改变了堆的结构 
//      stk.pop();
    }
    return pop_temp;
}
void prim(int x) {
    put_stk(x);
    while(!stk.empty() && cnt <= n) {
        ans += pop_stk();
//      cnt++;//
/*
如果你在pop_stk()中调用cnt++,那么可能会出现以下情况:

    你从优先队列中选择了一条边,并尝试将其对应的节点加入到生成树中。
    但是,由于某种原因(例如,该节点已经是一个环的一部分,或者该边与
    之前的某条边形成了更小的权重),你实际上并没有将这个节点加入到生
    成树中(即没有调用put_stk(push_temp);)。
    然而,由于你在pop_stk()中递增了cnt,你已经错误地表示了这个节点已
    经被加入到生成树中。

这会导致cnt的值比实际加入到生成树中的节点数量要大,进而可能导致算法在
错误的时间点终止(即当cnt > n时),从而得到一个不完整的最小生成树(或
者根本没有找到最小生成树)。

因此,为了确保cnt正确地表示了已经加入到最小生成树中的节点数量,你应该
在将节点加入到生成树中(即调用put_stk(x))时递增cnt,而不是在选择边(
即调用pop_stk())时递增。
*/
    }
}
int main() {
    cin>>n>>m;
    int u,v,w;
    for(int i=1; i<=m; i++) {
        scanf("%d %d %d",&u,&v,&w);
        insert_edge(u,v,w);
        insert_edge(v,u,w);
    }
    memset(vis,0,sizeof(vis));
    prim(1);
    if(cnt == n) cout<<ans;
    else cout<<"orz";
    return 0;
}

by NirvanaCeleste @ 2024-06-12 11:57:10

已结


|