题意:有两堆结点,其中每一堆已分别连上了若干边,要求:①分别在这两堆中同时连上在两堆中都未连上的边 ②不能连出回路,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;
}
利用并查集和Set解决两堆节点连接问题,确保无回路且每堆节点最多n-1条边。通过邻接矩阵或直接并查集判断未连接边,优化处理避免多余检查。代码实现包括特殊情况的处理分析。
5074

被折叠的 条评论
为什么被折叠?



