cf738 Div2-D1【并查集+set】

利用并查集和Set解决两堆节点连接问题,确保无回路且每堆节点最多n-1条边。通过邻接矩阵或直接并查集判断未连接边,优化处理避免多余检查。代码实现包括特殊情况的处理分析。

题意:有两堆结点,其中每一堆已分别连上了若干边,要求:①分别在这两堆中同时连上在两堆中都未连上的边 ②不能连出回路,n个结点的一堆至多有n-1条边
想法:n=1000,开俩邻接矩阵,由于无向图,只需判定矩阵的一半,将已有的边标记为1。枚举两个矩阵的这一半,如果两个都是0则说明这条边都没出现过, 将这条边加入集合s,加入的同时别忘了也要将这条边的结点加入并查集中,不然会处理不了回路的情况。
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 1010;
int n,g2[N][N],g1[N][N],m,k;
set<pair<int,int>>s1,s2,s;
int p1[N],p2[N];
int find1(int x)
{
    if(p1[x]!=x) p1[x]=find1(p1[x]);
    return p1[x];
}
int find2(int x)
{
    if(p2[x]!=x) p2[x]=find2(p2[x]);
    return p2[x];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){p1[i]=i;p2[i]=i;}
    int x1=m,x2=k;
    while (x1 -- )
    {
        int x,y;
        cin>>x>>y;
        if(x<y) swap(x,y);
        if(find1(x)!=find1(y)) p1[find1(x)]=find1(y);
        g1[x][y]=1;
    }
    while(x2--)
    {
        int x,y;
        cin>>x>>y;
        if(x<y) swap(x,y);
        if(find2(x)!=find2(y)) p2[find2(x)]=find2(y);
        g2[x][y]=1;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        {
            if(g1[i][j]==0) s1.insert({i,j});
            if(g2[i][j]==0) s2.insert({i,j});
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        {
            if(s1.count({i,j})&&s2.count({i,j})&&find1(i)!=find1(j)&&find2(i)!=find2(j))
            {
                p1[find1(i)]=find1(j);p2[find2(i)]=find2(j);
                s.insert({i,j});
            }
        }
    set<pair<int,int>>::iterator it1=s.begin(),it2=s.begin();
    if(n-1-max(m,k)>=s.size())
    {
        cout<<s.size()<<endl;
        for(it1;it1!=s.end();it1++) cout<<it1->first<<' '<<it1->second<<endl;
    }
    else
    {
        cout<<n-1-max(m,k)<<endl;
        int x=n-1-max(m,k);
        //cout<<m<<' '<<k;
        for(it2;it2!=s.end()&&x>=1;it2++) 
        {
            x--;
            cout<<it2->first<<' '<<it2->second<<endl;
        }
    }
    return 0;
}

其次有个小优化,其实根本不需要邻接矩阵判,直接并查集判更好,而且并查集也不需要写两次。输出时,我考虑了两种情况:①最多可加的边>=满足条件的边,直接输出满足条件的边即可 ②最多可加的边<满足条件的边,输出前n-1-max(a,b)【a、b分别为已有的边数】即可。
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 1010;
int n,m,k;
set<pair<int,int>>s1,s2,s;
int p[3][N];
int find(int d,int x)
{
    if(p[d][x]!=x) p[d][x]=find(d,p[d][x]);
    return p[d][x];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){p[1][i]=i;p[2][i]=i;}
    int x1=m,x2=k;
    while (x1 -- )
    {
        int x,y;
        cin>>x>>y;
        if(x<y) swap(x,y);
        if(find(1,x)!=find(1,y)) p[1][find(1,x)]=find(1,y);
    }
    while(x2--)
    {
        int x,y;
        cin>>x>>y;
        if(x<y) swap(x,y);
        if(find(2,x)!=find(2,y)) p[2][find(2,x)]=find(2,y);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        {
            if(find(1,i)!=find(1,j)) s1.insert({i,j});
            if(find(2,i)!=find(2,j)) s2.insert({i,j});
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        {
            if(s1.count({i,j})&&s2.count({i,j})&&find(1,i)!=find(1,j)&&find(2,i)!=find(2,j))
            {
                p[1][find(1,i)]=find(1,j);p[2][find(2,i)]=find(2,j);//勿忘!!!
                s.insert({i,j});
            }
        }
    if(n-1-max(m,k)>=s.size())
    {
        cout<<s.size()<<endl;
        for(auto it1:s) cout<<it1.first<<' '<<it1.second<<endl;
    }
    else
    {
        cout<<n-1-max(m,k)<<endl;
        int x=n-1-max(m,k);
        //cout<<m<<' '<<k;
        for(auto it2:s) 
        {
            if(x<1) continue;
            x--;
            cout<<it2.first<<' '<<it2.second<<endl;
        }
    }
    return 0;
}

但我们再仔细想想,“②最多可加的边<满足条件的边”这种情况一定是不存在的,因为s集合在加边时,要先判断加上这条边是不是有回路,如果有回路,一定不会加到s里,因此s里的边都是:①彼此无回路 ②与这一堆里已有的边也不会有回路的 ③与原有的加一起数量一定<=n-1条,因此输出时只有第一种情况,直接输出s里所有元素即可。
代码如下:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 1010;
int n,m,k;
set<pair<int,int>>s1,s2,s;
int p[3][N];
int find(int d,int x)
{
    if(p[d][x]!=x) p[d][x]=find(d,p[d][x]);
    return p[d][x];
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){p[1][i]=i;p[2][i]=i;}
    int x1=m,x2=k;
    while (x1 -- )
    {
        int x,y;
        cin>>x>>y;
        if(x<y) swap(x,y);
        if(find(1,x)!=find(1,y)) p[1][find(1,x)]=find(1,y);
    }
    while(x2--)
    {
        int x,y;
        cin>>x>>y;
        if(x<y) swap(x,y);
        if(find(2,x)!=find(2,y)) p[2][find(2,x)]=find(2,y);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
        {
            if(find(1,i)!=find(1,j)) s1.insert({i,j});
            if(find(2,i)!=find(2,j)) s2.insert({i,j});
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        {
            if(s1.count({i,j})&&s2.count({i,j})&&find(1,i)!=find(1,j)&&find(2,i)!=find(2,j))
            {
                p[1][find(1,i)]=find(1,j);p[2][find(2,i)]=find(2,j);
                s.insert({i,j});
            }
        }
    cout<<s.size()<<endl;
    for(auto it1:s) cout<<it1.first<<' '<<it1.second<<endl;
    return 0;
}
:root { --side-bar-bg-color: #fafafa; --control-text-color: #777; } @include-when-export url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,700,400&subset=latin,latin-ext); /* open-sans-regular - latin-ext_latin */ @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: normal; src: local('Open Sans Regular'), local('OpenSans-Regular'), url('./github/open-sans-v17-latin-ext_latin-regular.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* open-sans-italic - latin-ext_latin */ @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: normal; src: local('Open Sans Italic'), local('OpenSans-Italic'), url('./github/open-sans-v17-latin-ext_latin-italic.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* open-sans-700 - latin-ext_latin */ @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: bold; src: local('Open Sans Bold'), local('OpenSans-Bold'), url('./github/open-sans-v17-latin-ext_latin-700.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } /* open-sans-700italic - latin-ext_latin */ @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: bold; src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url('./github/open-sans-v17-latin-ext_latin-700italic.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD, U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } 怎么修改字体?
最新发布
03-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值