pku3352 图的关键边
Code
//题意:让你给图增加最少的边数,使得一旦图的某条边断掉后其它节点间仍然可达
//思路:利用缩图成树的方法,最终算出数的叶子数,(leafnum+1)/2就是答案了
#include <iostream>
using namespace std;
const int MAXN = 2010;
typedef struct
{//其实起点保存在边的下标中
int next;//该边指向的下一条边编号
int e;//边的终点
}Edge;
Edge edge[MAXN];
int root,N,M,eId;//边的编号
int p[MAXN];//不断更新下一条边编号
int low[MAXN];//某一连通分量中的最小层数,也即求出割点的层数
int d[MAXN],deep;//存储当前节点的遍历层数
int color[MAXN],color_num;//储存节点的颜色,以便区分是哪个连通分量
void Insert(int from,int to)
{
edge[eId].next = p[from];//该边指向的下一条边编号更新
edge[eId].e = to;
p[from] = eId++;
}
void DFS(int child,int father)
{
int s;
low[child] = d[child] = ++ deep;
for( s = p[child] ; s != -1 ; s = edge[s].next )//取边的编号
{
int e = edge[s].e;//取边终点
if( !d[e] )//未遍历过
{
DFS(e,child);
low[child] = __min(low[child],low[e]);//1、
}
else if( e != father )//不存在回边
low[child] = __min(low[child],d[e]);//2、
}//1和2有点类似于并查集,祖先为d最小的节点
}
void set_color(int r)
{
int s;
for( s = p[r] ; s != -1 ; s = edge[s].next )
{
int e = edge[s].e;
if( !color[e] )
{
if( low[e] > d[r] )//r和e有连边,可是r和e存在不同的连通分量中
color[e] = ++color_num;
else color[e] = color[r];//否则为同分量中,染为同一种颜色
set_color(e);//宽展去染
}
}
}
int main()
{
int i,j,a,b;
//freopen("3352.txt","r",stdin);
while( scanf("%d %d",&N,&M) != EOF )
{
eId = 0;
memset(p,-1,sizeof(p));
for( i = 0 ; i < M ; i ++ )
{
scanf("%d %d",&a,&b);
Insert(a,b);
Insert(b,a);//路是双向的
}
root = 1;
deep = 0;
memset(d,0,sizeof(d));
memset(low,0,sizeof(low));
DFS(root,-1);
memset(color,0,sizeof(color));
color_num = 1;
color[1] = 1;
set_color(root);
int degree[MAXN];//树节点的度
memset(degree,0,sizeof(degree));
for( i = 1 ; i <= N ; i ++ )
{
int a = color[i];
for( j = p[i] ; j != -1 ; j = edge[j].next )
{
int b = color[edge[j].e];
if( a != b )
{
degree[a] ++;
}
}
}
int leaf_num = 0;
for( i = 1 ; i <= color_num ; i ++ )
{
if( degree[i] == 1 )//入度为一的节点为叶子
leaf_num ++;
}
printf("%d\n",(leaf_num+1)/2);
}
return 0;
}
//题意:让你给图增加最少的边数,使得一旦图的某条边断掉后其它节点间仍然可达
//思路:利用缩图成树的方法,最终算出数的叶子数,(leafnum+1)/2就是答案了
#include <iostream>
using namespace std;
const int MAXN = 2010;
typedef struct
{//其实起点保存在边的下标中
int next;//该边指向的下一条边编号
int e;//边的终点
}Edge;
Edge edge[MAXN];
int root,N,M,eId;//边的编号
int p[MAXN];//不断更新下一条边编号
int low[MAXN];//某一连通分量中的最小层数,也即求出割点的层数
int d[MAXN],deep;//存储当前节点的遍历层数
int color[MAXN],color_num;//储存节点的颜色,以便区分是哪个连通分量
void Insert(int from,int to)
{
edge[eId].next = p[from];//该边指向的下一条边编号更新
edge[eId].e = to;
p[from] = eId++;
}
void DFS(int child,int father)
{
int s;
low[child] = d[child] = ++ deep;
for( s = p[child] ; s != -1 ; s = edge[s].next )//取边的编号
{
int e = edge[s].e;//取边终点
if( !d[e] )//未遍历过
{
DFS(e,child);
low[child] = __min(low[child],low[e]);//1、
}
else if( e != father )//不存在回边
low[child] = __min(low[child],d[e]);//2、
}//1和2有点类似于并查集,祖先为d最小的节点
}
void set_color(int r)
{
int s;
for( s = p[r] ; s != -1 ; s = edge[s].next )
{
int e = edge[s].e;
if( !color[e] )
{
if( low[e] > d[r] )//r和e有连边,可是r和e存在不同的连通分量中
color[e] = ++color_num;
else color[e] = color[r];//否则为同分量中,染为同一种颜色
set_color(e);//宽展去染
}
}
}
int main()
{
int i,j,a,b;
//freopen("3352.txt","r",stdin);
while( scanf("%d %d",&N,&M) != EOF )
{
eId = 0;
memset(p,-1,sizeof(p));
for( i = 0 ; i < M ; i ++ )
{
scanf("%d %d",&a,&b);
Insert(a,b);
Insert(b,a);//路是双向的
}
root = 1;
deep = 0;
memset(d,0,sizeof(d));
memset(low,0,sizeof(low));
DFS(root,-1);
memset(color,0,sizeof(color));
color_num = 1;
color[1] = 1;
set_color(root);
int degree[MAXN];//树节点的度
memset(degree,0,sizeof(degree));
for( i = 1 ; i <= N ; i ++ )
{
int a = color[i];
for( j = p[i] ; j != -1 ; j = edge[j].next )
{
int b = color[edge[j].e];
if( a != b )
{
degree[a] ++;
}
}
}
int leaf_num = 0;
for( i = 1 ; i <= color_num ; i ++ )
{
if( degree[i] == 1 )//入度为一的节点为叶子
leaf_num ++;
}
printf("%d\n",(leaf_num+1)/2);
}
return 0;
}