POJ3177-Redundant Paths

版权声明:[ EXP技术分享博客(http://exp-blog.com) ] 版权所有,转载请注明出处: https://blog.csdn.net/lyy289065406/article/details/6762432

全解题报告索引目录 -> 【北大ACM – POJ试题分类

转载请注明出处:http://exp-blog.com/2018/06/27/pid-1288/

-------------------------------------------------------------------------


大致题意:

       为了保护放牧环境,避免牲畜过度啃咬同一个地方的草皮,牧场主决定利用不断迁移牲畜进行喂养的方法去保护牧草。然而牲畜在迁移过程中也会啃食路上的牧草,所以如果每次迁移都用同一条道路,那么该条道路同样会被啃咬过度而遭受破坏。

       现在牧场主拥有F个农场,已知这些农场至少有一条路径连接起来(不一定是直接相连),但从某些农场去另外一些农场,至少有一条路可通行。为了保护道路上的牧草,农场主希望再建造若干条道路,使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。已知当前有的R条道路,问农场主至少要新建造几条道路,才能满足要求?

解题思路:

“使得每次迁移牲畜时,至少有2种迁移途径,避免重复走上次迁移的道路。”就是说当吧F个农场看作点、路看作边构造一个无向图G时,图G不存在桥。

那么可以建立模型:

       给定一个连通的无向图G,至少要添加几条边,才能使其变为双连通图。

这题是和POJ3352一模一样的题,只不过表述方式不同而已。

详细解题过程请参考我POJ3352的解题报告,有详细叙述。

传送门:

我的POJ3352解题报告:

http://blog.csdn.net/lyy289065406/article/details/6762370

看完POJ3353的解题报告,再回来看看本题要注意的一些地方:

本题和3353最大的区别就是,3353保证了没有重边,而本题有重边。

而3353的解题报告已经说过,当两点之间出现重边时,就不可以利用Low值去划分【边双连通分量】了,因为此时不同Low值的两点可能属于同一个【边双连通分量】!

那么如何解决重边的问题?

1、  构造图G时把重边也考虑进来,然后在划分边双连通分量时先把桥删去,再划分,其中桥的一端的割点归入当前正在划分的边双连通分量。这个处理比较麻烦;

2、  在输入图G的边时,若出现重边,则不把重边放入图G,然后在划分边双连通分量时依然用Low划分。

两者相权,当然是第2种方法更好处理了。

其实用邻接矩阵去存储图G的同学,是无需考虑重边的问题的。

但是用邻接链表去存储图G的同学就不得不考虑了,因为基本上都是用头插入法在链表中插入边的,所以无法检测重边。而改用尾插入法,则可以在指针从链头移到链尾的同时,顺便加一个判断,出现重边则不再插入,否则插入到尾部。

//Memory Time 
//236K   16MS  

#include<iostream>
using namespace std;

class Node
{
public:
	int id;
	class Node* next;
	Node():id(0),next(0){}
};

class solve
{
public:
	solve(int f,int r):F(f),R(r)
	{
		Initial();			
		Input_Creat();
		Tarjan(1,-1);		//本题给定的图G为连通的,因此从任意节点开始搜索均可
		printf("%d\n",BCC_SP_D_E());
	}
	~solve()
	{
		delete[] DFN;
		delete[] Low;
		delete[] degree;

		EmptyList();
	}

	int min(int a,int b) const{return a<b?a:b;}

	void Initial(void);				//申请存储空间并初始化
	void Input_Creat(void);			//输入并创建图G
	void AddEdge(int a,int b);		//向链表插入边a<->b (尾插入法,避免重边)

	void Tarjan(int s,int father);	//计算Low[]数组,用于寻找所有边双连通分量
	int BCC_SP_D_E(void);			//把每个边双连通分量(BCC)构造为缩点(SP),并计算每个缩点的度数(D)
									//返回值为使得图G为双连通所需添加的最少的边(E)的数量

	void DelLink(Node* p);			//释放以p为表头的整条链
	void EmptyList(void);			//释放邻接链表

protected:

	int F;					//the number of islands
	int R;					//the number of roads
	Node** LinkHead;		//邻接链表表头

	int TimeStamp;			//(外部)时间戳
	int* DFN;				//DFN[u]: 结点u的搜索次序(时间戳)
	int* Low;				//Low[u]: 结点u或u的子树能够追溯到的最早的栈中结点的次序号

	int* degree;			//记录每个缩点的总度数
};

void solve::Initial(void)
{
	LinkHead=new Node*[F+1];
	for(int i=1;i<=F;i++)
		LinkHead[i]=0;

	TimeStamp=0;
	DFN=new int[F+1];
	Low=new int[F+1];
	memset(DFN,0,sizeof(int)*(F+1));
	memset(Low,0,sizeof(int)*(F+1));

	degree=new int[F+1];
	memset(degree,0,sizeof(int)*(F+1));

	return;
}

void solve::Input_Creat(void)
{
	int a,b;
	Node* tmp;
	for(int j=1;j<=R;j++)
	{
		scanf("%d %d",&a,&b);

		if(!LinkHead[a])
			LinkHead[a]=new Node;
		if(!LinkHead[b])
			LinkHead[b]=new Node;

		AddEdge(a,b);
	}
	return;
}

void solve::AddEdge(int a,int b)
{
	Node* pa=LinkHead[a];
	Node* p1=new Node;
	p1->id=b;

	while(pa->next)
	{
		pa=pa->next;
		if(pa->id==p1->id)	//出现重边,重边不插入链表
			return;
	}
	pa->next=p1;

	Node* pb=LinkHead[b];
	Node* p2=new Node;
	p2->id=a;

	while(pb->next)			//能执行到这里说明a<->b不是重边
		pb=pb->next;		//直接搜索到链表尾部插入
	pb->next=p2;

	return;
}

void solve::Tarjan(int s,int father)
{
	DFN[s]=Low[s]=++TimeStamp;
	for(Node* p=LinkHead[s]->next;p;p=p->next)
	{
		int t=p->id;
		if(t!=father && DFN[t]<DFN[s])
		{
			if(DFN[t]==0)			//s->t为树枝边
			{
				Tarjan(t,s);
				Low[s]=min(Low[s],Low[t]);
			}
			else					//s->t为后向边
			{
				Low[s]=min(Low[s],DFN[t]);
			}
		}
	}
	return;
}

int solve::BCC_SP_D_E(void)
{
	for(int i=1;i<=F;i++)
		if(LinkHead[i])
		{
			for(Node* p=LinkHead[i]->next;p;p=p->next)	//枚举图G中每两个连通的点i<->j
			{											//由于图G为无向图,则连通是双向的
				int j=p->id;
				if(Low[i]!=Low[j])		//图G中Low值相同的两个点必定在同一个边双连通分量(即同一个缩点)中
				{						//检查i、j是否不在同一个缩点中

					degree[Low[i]]++;	//结点i所在的缩点的度+1
					degree[Low[j]]++;	//结点j所在的缩点的度+1
				}
			}
		}
	
	int leave=0;			//记录总度数=1(叶子)的缩点
	for(int k=1;k<=F;k++)	//枚举各个缩点的度数D
		if(degree[k]/2==1)	//由于是无向图,因此每个缩点的度都重复计算了2次,除2后才是真实的度数
			leave++;

	return (leave+1)/2;		//将一棵树连成一个边双连通分量至少需要添加的边数=(叶子节点数+1)/2
}

void solve::DelLink(Node* p)
{
	if(p->next)
		p=p->next;
	delete[] p;
	return;
}

void solve::EmptyList(void)
{
	for(int i=1;i<=F;i++)
		if(LinkHead[i])
			DelLink(LinkHead[i]);
	return;
}

int main(void)
{
	int f,r;
	while(scanf("%d %d",&f,&r)!=EOF)
		solve poj3177(f,r);

	return 0;
}


 

Sample Input

7 7

1 2

2 3

3 4

2 5

4 5

5 6

5 7

 

7 7

1 2

2 3

3 4

2 5

4 5

3 6

5 7

 

6 5

3 1

3 2

3 4

3 5

3 6

 

10 12

1 2

2 3

3 1

3 4

4 8

4 5

5 6

6 7

7 5

8 9

9 10

10 8

 

10 10

1 8

6 3

7 1

3 5

5 2

2 9

9 7

8 4

4 10

10 6

 

10 9

1 2

7 4

9 6

10 6

8 4

3 5

3 4

3 6

1 3

 

16 22

1 3

7 1

5 1

12 7

6 3

4 7

8 3

10 7

14 6

11 5

9 7

15 4

2 6

13 12

8 2

2 11

6 1

4 11

1 14

3 10

13 16

13 16

 

27 35

1 3

10 3

22 3

15 3

11 15

5 15

12 22

18 10

23 11

7 1

2 15

25 1

14 10

24 11

8 2

19 22

4 12

16 4

13 18

9 14

21 13

6 4

17 23

20 17

17 6

3 21

20 3

9 13

17 12

20 18

2 26

26 27

27 8

2 27

26 8

 

75 81

1 3

58 3

37 1

36 58

15 36

9 58

10 37

8 1

44 10

33 44

61 8

54 9

4 3

20 44

53 37

26 20

67 54

71 20

5 3

70 54

45 67

14 54

74 67

41 53

52 37

63 53

31 20

55 63

60 55

40 5

75 58

62 31

68 52

72 36

49 9

66 31

43 41

22 52

35 8

21 45

30 15

11 61

7 68

57 30

12 40

27 71

25 40

46 66

42 61

24 37

29 4

59 11

16 74

47 5

69 74

64 59

56 75

19 9

48 56

23 9

13 72

2 43

32 1

73 13

28 7

6 45

18 4

38 42

50 72

17 53

39 50

51 63

34 25

65 64

67 41

58 5

5 27

75 63

7 50

20 18

38 65

 

200 250

1 3

106 1

134 1

157 134

23 106

60 134

44 60

117 1

126 1

11 134

139 44

178 3

97 60

101 157

118 44

30 23

128 30

174 3

108 23

110 128

132 157

92 106

173 132

79 106

82 178

7 44

52 79

74 30

4 23

49 7

164 139

127 30

156 4

65 7

120 101

46 97

112 178

8 46

59 60

198 174

100 134

90 92

192 60

125 100

26 178

19 192

63 125

155 126

70 100

35 63

151 126

165 157

146 70

84 157

141 52

160 70

163 8

38 127

171 139

62 101

133 11

177 146

158 125

41 165

145 52

98 30

5 177

68 164

168 173

107 178

86 132

199 127

136 168

71 155

50 128

189 35

193 46

105 70

195 189

89 158

69 177

190 50

28 19

21 92

93 71

170 86

122 21

131 136

197 158

16 108

33 195

18 164

196 141

94 92

61 79

149 26

169 193

124 163

78 189

147 108

150 49

129 70

77 168

194 18

54 100

140 127

24 196

109 158

2 97

17 195

64 28

115 174

185 41

81 141

45 62

180 18

167 109

27 65

123 140

188 77

91 129

73 110

76 173

14 149

103 105

51 11

57 84

58 101

148 193

43 156

162 109

22 61

179 52

67 74

200 117

6 167

119 192

113 41

184 16

32 98

39 160

75 32

175 98

121 78

183 26

47 174

102 79

83 23

172 127

176 74

138 121

182 90

29 156

153 183

114 162

152 47

15 136

12 64

143 155

161 89

99 90

87 114

25 193

144 86

137 64

135 52

56 14

55 112

20 71

142 5

34 126

116 56

40 79

130 89

187 49

85 62

111 136

191 39

166 16

159 120

13 50

95 55

154 33

96 171

181 115

88 21

80 24

48 14

72 21

31 67

9 31

66 143

37 117

104 56

36 86

42 125

186 33

10 184

53 18

164 64

136 63

77 25

128 105

133 147

130 1

67 161

10 132

190 173

195 80

123 1

70 82

126 38

163 7

193 17

152 105

44 24

168 185

174 163

177 40

79 173

70 19

26 60

198 130

97 22

143 67

97 25

119 89

194 163

188 180

49 173

109 71

4 124

58 79

151 178

74 93

34 96

161 65

167 16

172 114

183 14

46 116

199 187

118 175

109 23

101 115

160 114

110 173

96 28

77 182

27 116

 

14 16

1 2

1 12

12 2

3 2

4 3

4 5

4 6

6 14

7 14

7 6

7 8

7 9

10 9

11 10

11 13

13 10

Sample Output

2

2

3

2

0

3

2

4

16

32

2

猜你喜欢

转载自blog.csdn.net/lyy289065406/article/details/6762432