【最小生成树】口袋的天空

题目

kruskal是怎么都行,但是prim需要先建好最小树,然后又减去最大的几条边

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5e3+10;
const int M = 2e5+10;

struct edge
{
    int u, v, w;
} e[M];
//MergeFindSet
int fa[N];
int Get(int x)
{
    if(fa[x] == x)
        return x;
    return fa[x] = Get(fa[x]);
}
inline void MergeFindSetInit(int &n)
{
    for(int i = 1; i<= n; i++)
        fa[i] = i;
}
bool merge(int u, int v)
{
    int fu = Get(u);
    int fv = Get(v);
    if(fu != fv)
    {
        fa[fv] = fu;
        return true;
    }
    else
        return false;
}
//Kruskal
bool Cmp(edge a, edge b)
{
    return a.w < b.w;
}
int Kruskal(int n, int m, int k)
{
    int sum = 0;
    MergeFindSetInit(n);
    sort(e+1, e+m+1, Cmp);
    int cnt = 0;
    for(int i = 1; i <= m; i++)
    {
        if(cnt == n-k)
            break;
        if(merge(e[i].u, e[i].v))
        {
            cnt++;
            sum += e[i].w;
        }
    }
    if(cnt == n-k) return sum;
    return -1;
}
//in
inline void In(int &n, int &m, int &k)
{
    cin >> n >> m >> k;
    for(int i = 1; i <= m; i++)
    {
        cin >> e[i].u >> e[i].v >> e[i].w;
    }
}
int main()
{
    int n, m, k;
    In(n, m, k);
    int ans = Kruskal(n, m, k);
    if(ans == -1)
        cout <<"No Answer";
    else
        cout << ans << endl;
    return 0;
}
View Code

 

 

 

直接prim不行的数据

4 4 2

1 2 1

3 4 1

1 3 100

2 4 100

但是先建树是可行的,观摩别人代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
struct EDGE
{
    int out,len,nxt;
} edge[20005];
struct cmp1
{
    bool operator () (EDGE &a,EDGE &b)
    {
        return a.len > b.len;
    }
};
struct cmp2
{
    bool operator () (EDGE &a,EDGE &b)
    {
        return a.len < b.len;
    }
};
priority_queue <int,vector<EDGE>,cmp1> que;
priority_queue <int,vector<EDGE>,cmp2> Ans;
int n,m,d,tot,ans,cnt;
int head[1005],marks[1005],mark[1005],dis[1005],vis[1005];
void dfs(int pos);
void prim(int sta);
void dfs(int pos)
{
    marks[pos] = cnt;
    for(int i = head[pos]; i != 0; i = edge[i].nxt)
    {
        if(marks[edge[i].out] != 0)
            continue;
        dfs(edge[i].out);
    }
}
void prim(int sta)
{
    memset(dis,127,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[sta] = 0;
    EDGE cc;
    cc.out = sta,cc.len = 0;
    que.push(cc);
    while(!que.empty())
    {
        int u = que.top().out;//当然使用优先队列来求生成树啦 nlogn
        que.pop();
        if(vis[u] == 1)
            continue;
        vis[u] = 1;
        for(int i = head[u]; i != 0; i = edge[i].nxt)
        {
            int v = edge[i].out,len = edge[i].len;
            if(vis[v] == 0 && dis[v] > len)
            {
                dis[v] = len;
                que.push((EDGE)
                {
                    v,dis[v],0
                });
            }
        }
    }
    for(int i = 1; i <= n; i ++)
    {
        if(marks[i] == marks[sta])
        {
            ans += dis[i];
            Ans.push((EDGE)
            {
                0,dis[i],0
            });
        }
    }
}
int main ()
{
    //freopen("1195.in","r",stdin);
    //freopen("1195.out","w",stdout);
    scanf("%d%d%d",&n,&m,&d);
    for(int i = 1; i <= m; i ++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        edge[++ tot].nxt = head[a],head[a] = tot,edge[tot].out = b,edge[tot].len = c;
        edge[++ tot].nxt = head[b],head[b] = tot,edge[tot].out = a,edge[tot].len = c;
    }
    for(int i = 1; i <= n; i ++)
    {
        if(marks[i] != 0)
            continue;
        cnt ++;
        dfs(i);
    }//DFS求联通块个数,顺便把每个联通块标号为cnt
    if(cnt > d)
    {
        printf("No Answer");
        return 0;
    }
    for(int i = 1; i <= n; i ++)
    {
        if(mark[marks[i]] == 0) //判断当前联通块是否已建树
        {
            prim(i);
            mark[marks[i]] = 1;
        }
    }//prim算法求最小生成树
    for(int i = 1; i <= d - cnt; i ++)
    {
        if(!Ans.empty()) //把最大的几条边推出
        {
            ans -= Ans.top().len;
            Ans.pop();
        }
        else //当发现无边可出却还不能连成k个联通块时,退出
        {
            printf("No Answer");
            return 0;
        }
    }
    printf("%d",ans);
    return 0;
}
View Code

 

posted @ 2021-06-30 11:33  bear_xin  阅读(19)  评论(0)    收藏  举报