总体来说
点我的上一篇文章熟悉一下并查集的应用
首先这题比较常规,不知道为什么会被标注为困难。
提醒,此题一定要注意由于存在两人都能通过的路,
所以在寻找路径的时候首先要从两人共同的路上下手。
因为这样才能够使得多余的路更多的在单人路上,这样
才能删除更多的路径使得最终得到的结果更大。
废话不多的说(尽管已经说很多了),进入正题
首先讲一下我的代码整体思路
我们最终的目标是得到删除最多的边并不使新图失去连通性;
初始化两个并查集Alice和Bob;
- 当我们遇到前两种类型的边时,分别将判断两人对应的连通性并考虑删除该边。
- 而遇到第三种类型的边时,同时判断两人的连通性并考虑删除该边。
我们可以这样:
对于新得到的每组边,判断边的类型,如果是1或2就先不管。
对于第三类型的边,判断在加入此条边之前原图是否已经具有连通性,
Alice.find(p1)==Alice.find(p2);
Bob.find(p1)==Bob.find(p2);
如果具有连通性,即为以上两式均为真,则表示该第三类型的边是可以删除的。
我们将最后的待输出结果加1;
否则,要执行以下命令:Alice.un(p[1],p[2]);
Bob.un(p[1],p[2]);
表示将该第三类型的边加入图中
for(int[] p:edges){
if(p[0]==3){
if(a.find(p[1])==a.find(p[2]) && b.find(p[1])==b.find(p[2]))
res++;
else{
a.un(p[1],p[2]);
b.un(p[1],p[2]);}
}
}
当我们把第三种类型的边都处理完了后再处理前两种类型的边。
并且可以同时处理,因为两种类型相互不影响。
for(int[] p:edges){
if(p[0]==1){
if(a.find(p[1])==a.find(p[2]))
res++;
else a.un(p[1],p[2]);
}
if(p[0]==2){
if(b.find(p[1])==b.find(p[2]))
res++;
else b.un(p[1],p[2]);
}
}
到此,我们总算是考虑完成了联通的情况,但是对于本身就不连通的图来说,
以上的流程根本判断不出来,所以还得另在外判断。
由于我们使用了并查集,而并查集本身就有判断连通分支的简便方法。
就是,通过对比数组Uf.id,即判断祖宗节点的个数,
- 如果Uf.id[i]==i为真,则我们就说该节点为祖宗节点。
- 而对于两条路来说,如果联通则每条路只有一个祖宗节点。
int r1=0,r2=0;
for(int i=1;i<n+1;i++){
if(a.id[i]==i)
r1++;
if(b.id[i]==i)
r2++;
}
据此我么就可以判断原图有没有连通性了。
return (r1==r2 && r1==1)?res:-1;
代码
class Solution {
class Uf{
int[] id;
public Uf(int n){
id=new int[n+1];
for(int i=1;i<n+1;i++){
id[i]=i;
}
}
public int find(int m){
while(m!=id[m]){
id[m]=id[id[m]];
m=id[m];
}
return m;
}
public void un(int m,int n){
id[find(m)]=find(n);
}
}
public int maxNumEdgesToRemove(int n, int[][] edges) {
Uf a=new Uf(n);
Uf b=new Uf(n);
int res=0;
for(int[] p:edges){
if(p[0]==3){
if(a.find(p[1])==a.find(p[2]) && b.find(p[1])==b.find(p[2]))
res++;
else{
a.un(p[1],p[2]);
b.un(p[1],p[2]);}
}
}
for(int[] p:edges){
if(p[0]==1){
if(a.find(p[1])==a.find(p[2]))
res++;
else a.un(p[1],p[2]);
}
if(p[0]==2){
if(b.find(p[1])==b.find(p[2]))
res++;
else b.un(p[1],p[2]);
}
}
int r1=0,r2=0;
for(int i=1;i<n+1;i++){
if(a.id[i]==i)
r1++;
if(b.id[i]==i)
r2++;
}
return (r1==r2 && r1==1)?res:-1;
}
}