题意描述: 公园有n个景点,公园的管理员计划要建m条道路,并且安排一些形成回路的参观路径,如果一条道路可以被多条回路共用, 那么这条边是冲突边,如果一个块中有多个环,则该块中的每条边都是冲突边。
如果不能形成环的路则为不需要的边,现在就是求无向图中冲突边和不需要边的条数
解题思路:
把图分为多个块,然后判断每个块里面的边数,如果块的边数等于块的点数,那么该块只有一个环,如果块的边数大于块的 点数,那么这个块中有多个环,并且这个块中的每条边都是多个环里面的一部分。
注意: 将块出栈时,只能出到u的子节点v为止 ,因为u作为割点极有可能是该个块与别的块公用的,所以若是每次出栈到u,则会破坏其他的块。(这就是与有向图的tarjan的一个不同之处。)
代码:
#include<iostream>#include<string.h>#include<vector>#define max 10010using namespace std;vector <int> vec[max];int in[max], stack[max] , block[max];int low[max], dfn[max];int top , ans1, ans2 ,step;int n, m;void init(){ for(int i=0;i<max;i++) { low[i]=dfn[i]=stack[i]=0; vec[i].clear(); } top=-1 ; step=ans1=ans2=0;}void insert(int u, int v){ vec[u].push_back(v); vec[v].push_back(u);}void deal_circle(){//cout<<block[0]<<endl<<endl; int num=0; for(int i=1;i<=block[0];i++) {//cout<<block[i]<<endl; for(int j=0;j<vec[block[i]].size();j++) { int v=vec[block[i]][j]; if(in[v]) { num++; } //如果v属于当前块,则将边数累加一次 } } num/=2;//因为每条边的顶点有两个,且都属于该块中,所以每条边被重复计算了一次 if(block[0]>num) ans1+=num; else if(block[0]<num) ans2+=num; //cout<<"ans1= "<<ans1<<" ans2= "<<ans2<<endl;}void tarjan(int u ){ int v; dfn[u]=low[u]=++step; stack[++top]=u; for(int i=0;i<vec[u].size(); i++) { v=vec[u][i]; if(!dfn[v]) //若该点没有被访问过 { tarjan(v); low[u]=min(low[u], low[v]); if(low[v]>=dfn[u]) //说明u的子节点没有与u的祖先相连的后向边,即u为割项 { block[0]=0; //cout<<endl<<endl; memset(in,0,sizeof(in)); int t; do { t=stack[top--]; block[++block[0]]=t; in[t]=1; //cout<<u<<"-----"<<t<<endl; }while(v!=t); block[++block[0]]=u; in[u]=1; deal_circle(); } } else low[u]=min(dfn[v] , low[u]); }}int main(){ int a, b; while(~scanf("%d%d",&n,&m) && n+m ) { init(); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); insert(a,b); } for(int i=0;i<n;i++) if(!dfn[i]) tarjan(i); printf("%d %d\n",ans1,ans2); } return 0;}