P1656 炸铁路
题目:
题解思路:
//大概题解思路:通过枚举的方式,在已知的m条道路中选出一条进行断开,相当于在建立道路联系的时候不将这条道路连上
//然后连接剩余的道路(实质上进行m次这种操作 每次都有一条道路不连接)
//误区:很容易被题目给带偏思路,会考虑先将所有的点通过道路连接,然后再选择炸毁,但不好实现的地方在于炸毁道路两边的点是有同一个祖先
//如何对这些剩余的点重现建立新的祖先,因此我们把这部分归结到修建道路时不建立这条(重复进行m次操作)
代码:
#include<iostream> #include<algorithm> using namespace std; const int maxn=150+10; const int maxx=5000+10; int n,m; int a[maxn],b[maxn]; int par[maxn]; //储存祖先结点 int rank1[maxn]; //储存结点所在的高度 // int inde=0; struct node{ int a; int b; }temp[maxx]; bool cmp(struct node tt,struct node kk){ //从小到大进行排序 if(tt.a!=kk.a){ //先比较a 然后比较b return tt.a<kk.a; }else{ return tt.b<kk.b; } } void init(){ //初始化 for(int i=1;i<=n;i++){ // par[i]=i; rank1[i]=0; } } int find(int x){ //查找x祖先 if(par[x]==x) return x; //如果x的祖先就是自己直接返回 else return par[x]=find(par[x]); //如果x的祖先不是自己 则需要通过find函数递归找到x的祖先 同时对x的父亲们也一起顺便找共同祖先 } void unite(int x,int y){ x=find(x); y=find(y); if(x==y) return; if(rank1[x]>rank1[y]){ par[y]=x; }else{ par[x]=y; if(rank1[x]==rank1[y]){ rank1[y]++; } } } bool same(int x,int y){ if(find(x)==find(y)) return true; else return false; } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ int a,b; cin>>a>>b; if(a<=b){ //首先这些路的编号进行排序 方便后续直接输出 temp[i].a=a; temp[i].b=b; }else{ temp[i].a=b; temp[i].b=a; } } sort(temp+1,temp+m+1,cmp); for(int i=1;i<=m;i++){ //枚举m次 init(); for(int j=1;j<=m;j++){ //第i条道路不建立 if(j!=i){ unite(temp[j].a,temp[j].b); //道路连接 } } //通过判断所有的par是否相同 相同即为有同一个祖先 //或许可以不要下面代码 int tt=0; for(int j=1;j<=n;j++){ //目标不是为了查询祖先 只是为了把所有的祖先进行建立 tt=find(j); } int t=par[1]; int flag=0; for(int j=2;j<=n;j++){ if(t!=par[j]){ //代表有不同的祖先 flag=1; break; } } if(flag){ cout<<temp[i].a<<" "<<temp[i].b<<endl; } } return 0; }