最小生成树——Kruskal算法

Powered by:AB_IN 局外人

P3366 【模板】最小生成树

还是喜欢用Kruskal。
不加注释了,板子。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;
int n,m,u,v,w,ans,cnt;

int fa[maxn];

struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    rep(i,1,m) cin>>e[i].u>>e[i].v>>e[i].w;
    sort(e+1,e+1+m,cmp);
    rep(i,1,n) fa[i]=i;
    rep(i,1,m){
        if(cnt==n-1) break;
        w=e[i].w; u=find(e[i].u);v=find(e[i].v);
        if(u!=v){
            fa[u]=v;
            ans+=w;
            cnt++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

P1547 [USACO05MAR]Out of Hay S

求最小生成树的最长边。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;
int n,m,u,v,w,ans,cnt;

int fa[maxn];

struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    rep(i,1,m) cin>>e[i].u>>e[i].v>>e[i].w;
    sort(e+1,e+1+m,cmp);
    rep(i,1,n) fa[i]=i;
    rep(i,1,m){
        if(cnt==n-1) break;
        w=e[i].w; u=find(e[i].u);v=find(e[i].v);
        if(u!=v){
            fa[u]=v;
            ans=max(ans,w);
            cnt++;
        }
    }
    cout<<ans<<endl;
    return 0;
}

P1195 口袋的天空

简单说就是会有 k k k颗树。
c n t = = n − k cnt==n-k cnt==nk时就满足条件了。
(一般我们是一个数,所以是 c n t = = n − 1 cnt==n-1 cnt==n1

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;
int n,m,u,v,w,ans,cnt,k;

int fa[maxn];

struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m>>k;
    rep(i,1,m) cin>>e[i].u>>e[i].v>>e[i].w;
    sort(e+1,e+1+m,cmp);
    rep(i,1,n) fa[i]=i;
    rep(i,1,m){
        if(cnt==n-k) {cout<<ans<<endl;return 0;}
        w=e[i].w; u=find(e[i].u);v=find(e[i].v);
        if(u!=v){
            fa[u]=v;
            ans+=w;
            cnt++;
        }
        
    }
    cout<<"No Answer"<<endl;
    return 0;
}

P2820 局域网

先正常求最小生成树。
用题目给的所有边的权值 减去 最小生成树的权值即可。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;
int n,m,u,v,w,ans,cnt,sum;

int fa[maxn];

struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    rep(i,1,m) cin>>e[i].u>>e[i].v>>e[i].w,sum+=e[i].w;
    sort(e+1,e+1+m,cmp);
    rep(i,1,n) fa[i]=i;
    rep(i,1,m){
        if(cnt==n-1) break;
        w=e[i].w; u=find(e[i].u);v=find(e[i].v);
        if(u!=v){
            fa[u]=v;
            ans+=w;
            cnt++;
        }
    }
    cout<<sum-ans<<endl;
    return 0;
}

NEFU205 最小树1

加注释的板子。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;
int n,m,u,v,cnt;
float w,ans;
int fa[maxn];

struct sa{
    int u,v;
    float w;//权值
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n&&n){
            ans=0.0;cnt=0;
            m=n*(n-1)/2;
            rep(i,1,m) cin>>e[i].u>>e[i].v>>e[i].w;
            sort(e+1,e+1+m,cmp);//按权值排序
            rep(i,1,n) fa[i]=i;
            rep(i,1,m){
                if(cnt==n-1) break;//如果边数为(顶点数-1),所有边就都弄完了,而且满足树的定义。
                w=e[i].w; u=find(e[i].u);v=find(e[i].v);
                if(u!=v){//如果祖先不同,则归为相同
                    fa[u]=v;
                    ans+=w;
                    cnt++;//边数加一。
                }
            }
            printf("%.2f\n",ans);
    }
    return 0;
}

NEFU209 湖南修路

先是cmp函数:先用 j u d g e judge judge排,再用 w w w排,都是大的排在后面。
然后用 c o u n t e r counter counter记录有多少个 j u d g e = 1 judge=1 judge=1的,把他们先并起来。
再以这些边为基准生成最小生成树,当 c n t = = n − 1 − c o u n t e r cnt==n-1-counter cnt==n1counter时跳出。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200;
int n,counter,u,v,w,ans,cnt;

int fa[maxn];

struct sa{
    int u,v,w,judge;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    if(x.judge!=y.judge) return x.judge<y.judge;
    else return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n){
        counter=0;cnt=0;ans=0;
        rep(i,1,n) fa[i]=i;
        rep(i,1,n*(n-1)/2)
        {
            cin>>e[i].u>>e[i].v>>e[i].w>>e[i].judge;
            u=find(e[i].u);v=find(e[i].v);
            if(e[i].judge && u!=v)
                {fa[find(u)]=find(v);counter++;}
        }
        sort(e+1,e+1+(n*(n-1)/2),cmp);
        rep(i,1,(n*(n-1)/2)-counter){
            if(cnt==n-1-counter) break;
            w=e[i].w; u=find(e[i].u);v=find(e[i].v);
            if(u!=v){
                fa[u]=v;
                ans+=w;
                cnt++;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

NEFU129 修路工程

板子。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;
int n,m,u,v,cnt;
int w,ans;
int fa[maxn];

struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n>>m&&n){
            ans=0;cnt=0;
            rep(i,1,n) cin>>e[i].u>>e[i].v>>e[i].w;
            sort(e+1,e+1+n,cmp);
            rep(i,1,m) fa[i]=i;
            rep(i,1,n){
                if(cnt==m-1) break;
                w=e[i].w; u=find(e[i].u);v=find(e[i].v);
                if(u!=v){
                    fa[u]=v;
                    ans+=w;
                    cnt++;
                }
            }
            if(cnt==m-1) cout<<ans<<endl;
            else cout<<"?"<<endl;
    }
    return 0;
}

NEFU1525 一道图论一

首先得先明白路径上权值最大的一条边权值最小这句话什么意思。
就是 s − > t s->t s>t有很多路径,每个路径都用一个边权值的最大值。
我们现在需要求的是这些最大值里面的最小值。
其实就是生成最小生成树。
因为边权值就从小到大排的。
当s和t在同一个集合时,最后那个边的权值便是答案。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200005;

int n,m,u,v,x,y;
int k,w;
int fa[maxn];

struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n>>m>>k){
            rep(i,1,m) cin>>e[i].u>>e[i].v>>e[i].w;
            sort(e+1,e+1+m,cmp);
            while(k--){
                int flag=0;
                long long ans=0;
                cin>>x>>y;
                rep(i,1,n) fa[i]=i;
                rep(i,1,m){
                    w=e[i].w; u=find(e[i].u);v=find(e[i].v);
                    if(u!=v){
                        fa[u]=v;
                    }
                    if(find(x)==find(y))
                    {
                        flag=1;
                        ans=e[i].w;
                        break;
                    }
                }
                flag==1?printf("%lld\n",ans):printf("-1\n");
            }
    }
    return 0;
}

NEFU1791 藤原千花的星星图

没用按秩合并,直接是快读。302ms过的。

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
typedef long long ll;
using namespace std;
const ll maxn=2e6+10;
int n,m,u,v,cnt,w;
ll ans;
int fa[maxn];

namespace IO{
    char ibuf[1<<21],*ip=ibuf,*ip_=ibuf;
    char obuf[1<<21],*op=obuf,*op_=obuf+(1<<21);
    inline char gc(){
        if(ip!=ip_)return *ip++;
        ip=ibuf;ip_=ip+fread(ibuf,1,1<<21,stdin);
        return ip==ip_?EOF:*ip++;
    }
    inline void pc(char c){
        if(op==op_)fwrite(obuf,1,1<<21,stdout),op=obuf;
        *op++=c;
    }
    inline int read(){
        register int x=0,ch=gc(),w=1;
        for(;ch<'0'||ch>'9';ch=gc())if(ch=='-')w=-1;
        for(;ch>='0'&&ch<='9';ch=gc())x=x*10+ch-48;
        return w*x;
    }
    template<class I>
    inline void write(I x){
        if(x<0)pc('-'),x=-x;
        if(x>9)write(x/10);pc(x%10+'0');
    }
    class flusher_{
    public:
        ~flusher_(){if(op!=obuf)fwrite(obuf,1,op-obuf,stdout);}
    }IO_flusher;
}
using namespace IO;





struct sa{
    int u,v,w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    while(scanf("%d%d",&n,&m)!=-1){
            ans=0;cnt=0;
            rep(i,1,m) {e[i].u=read();e[i].v=read();e[i].w=read();}
            sort(e+1,e+1+m,cmp);
            rep(i,1,n) fa[i]=i;
            rep(i,1,m){
                if(cnt==n-1) break;
                w=e[i].w; u=find(e[i].u);v=find(e[i].v);
                if(u!=v){
                    fa[u]=v;
                    ans+=w;
                    cnt++;
                }
            }
            cnt==n-1?printf("%lld\n",ans):printf("-1\n");
    }
    return 0;
}

这是按秩合并的函数

void join(int a,int b)
{
    int a1=find(a),b1=find(b);
    if(rk[a1]>rk[b1])swap(a1,b1);
    fa[a1]=b1;
    if(rk[a1]==rk[b1])rk[b1]++;
}
void init()
{
	for(int i=1;i<=n;i++)
     {fa[i]=i;rk[i]=0;}
}

NEFU211 小希的迷宫

可能会多个树,要排除这种情况。

#include 
using namespace std;
int a,b,cnt,flag,fa[100001],vis[100001];
int find(int x)
{
    if(x!=fa[x])fa[x]=find(fa[x]);
    return fa[x];
}
void join(int a,int b)
{
    int a1=find(a),b1=find(b);
    if(a1!=b1){fa[a1]=b1;flag=0;}
    else flag=1;//又连一遍就成环了
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>a>>b&&!(a==-1&&b==-1))
    {
        if(a==0&&b==0){printf("Yes\n");continue;}//a,b都是0,也得输出yes
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=100001;i++) fa[i]=i;
        cnt=flag=0;
        while(!(a==0&&b==0))
        {
            vis[a]=vis[b]=1;
            if(!flag)join(a,b);
            cin>>a>>b;
        }
        if(flag) {printf("No\n");continue;}
        for(int i=1;i<=100001;i++)
        if(vis[i]&&fa[i]==i) cnt++;
        cnt==1?printf("Yes\n"):printf("No\n");
    }
    return 0;
}

NEFU206 最小树2

重点就是字母顺序装换成数字
n u m b e r = u p p e r   c a s e   l e t t e r s − ′ A ′ + 1 number=upper\ case\ letters-'A'+1 number=upper case lettersA+1
n u m b e r = l o w e r   c a s e   l e t t e r s − ′ a ′ + 1 number=lower\ case\ letters-'a'+1 number=lower case lettersa+1
通用的是
n u m b e r = l e t t e r s   &   31 number=letters \ \&\ 31 number=letters & 31

#include 
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
using namespace std;
const int maxn=200;
int n,m,w,ans,cnt,u,v;
char a,b;
int fa[maxn];

struct sa{
    int u,v;
    int w;
}e[maxn];

bool cmp(struct sa x,struct sa y){
    return x.w<y.w;
}

int find(int x) {
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    while(cin>>n>>m){
        cnt=0;ans=0;
        rep(i,1,m) {cin>>a>>b; e[i].u=a-'A'+1; e[i].v=b-'A'+1; cin>>e[i].w;}
        sort(e+1,e+1+m,cmp);
        rep(i,1,n) fa[i]=i;
        rep(i,1,m){
            if(cnt==n-1) break;
            w=e[i].w; u=find(e[i].u);v=find(e[i].v);
            if(u!=v){
                fa[u]=v;
                ans+=w;
                cnt++;
            }
        }
        if(cnt==n-1) cout<<ans<<endl;
        else cout<<"-1"<<endl;
    }
    return 0;
}

完结。

你可能感兴趣的:(ACM,kruskal)