数据结构(陈越)PAT练习题 第五周:图(上)

来源:互联网 发布:mql4编程手册 编辑:程序博客网 时间:2024/06/05 03:27

05-1. List Components

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
CHEN, Yue

For a given undirected graph with N vertices and E edges, please list all the connected components by both DFS and BFS. Assume that all the vertices are numbered from 0 to N-1. While searching, assume that we always start from the vertex with the smallest index, and visit its adjacent vertices in ascending order of their indices.

Input Specification:

Each input file contains one test case. For each case, the first line gives two integers N (0<N<=10) and E, which are the number of vertices and the number of edges, respectively. Then E lines follow, each described an edge by giving the two ends. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in each line a connected component in the format "{ v1 v2 ... vk }". First print the result obtained by DFS, then by BFS.

Sample Input:
8 60 70 12 04 12 43 5
Sample Output:
{ 0 1 4 2 7 }{ 3 5 }{ 6 }{ 0 1 2 7 4 }{ 3 5 }{ 6 }

这一题是关于图的DFS和BFS的基本练习了,没什么特别复杂的。我用的是矩阵来表示图,主要是为了代码写起来方便。矩阵有n行n列,它存储在一维数组中,矩阵的第i行第j列元素对应的数组下标为i*n+j。DFS和BFS可以参考姥姥的伪代码写出来。姥姥说树是一种特殊的图,其实树的先序遍历就是一种DFS,而树的层序遍历就是一种BFS。在BFS中需要用到队列,C++的STL封装了队列容器,可以直接拿来用(STL真的是个好东西啊~)。下面是完整的代码:

#include <iostream>#include <queue>using namespace std;int n, e;int* a;bool* visited;void DFS( int j ){cout << j << ' ';visited[j] = true;for( int i=0; i<n; ++i ){if( a[i*n+j]==1 && !visited[i] )DFS( i );}}int main(){cin >> n >> e;a = new int [n*n];for( int i=0; i<n*n; ++i )a[i] = 0;visited = new bool [n];for( int i=0; i<n; ++i )visited[i] = false;//--input edge--for( int k=0; k<e; ++k ){int i,j;cin >> i >> j;a[i*n+j] = 1;a[j*n+i] = 1;}//---DFS---bool print = false;for( int j=0; j<n; ++j ){if( !visited[j] ){print = true;if( j!=0 ) cout << endl;cout << "{ " << j << " ";visited[j] = true;}for( int i=0; i<n; ++i ){if( a[i*n+j]==1 && !visited[i] )DFS( i );}if( print ){cout << '}';print = false;}}//== prepare for BFS ==print = false;for( int i=0; i<n; ++i )visited[i] = false;queue<int>q;//-- BFS --for( int j=0; j<n; ++j ){if( !visited[j] ){print = true;cout << "\n{ " << j << " ";visited[j] = true;q.push( j );}while( !q.empty() ){for( int i = q.front(),k=0; k<n; ++k ){if( !visited[k] && a[i*n+k]==1 ){visited[k] = true;cout << k << " ";q.push( k );}}q.pop();}if( print ){cout << '}';print = false;}}delete [] a;delete [] visited;return 0;}


05-2. Saving James Bond - Easy Version

时间限制
200 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard
作者
CHEN, Yue

This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the world's most famous spy, was captured by a group of drug dealers. He was sent to a small piece of land at the center of a lake filled with crocodiles. There he performed the most daring action to escape -- he jumped onto the head of the nearest crocodile! Before the animal realized what was happening, James jumped again onto the next big head... Finally he reached the bank before the last crocodile could bite him (actually the stunt man was caught by the big mouth and barely escaped with his extra thick boot).

Assume that the lake is a 100 by 100 square one. Assume that the center of the lake is at (0,0) and the northeast corner at (50,50). The central island is a disk centered at (0,0) with the diameter of 15. A number of crocodiles are in the lake at various positions. Given the coordinates of each crocodile and the distance that James could jump, you must tell him whether or not he can escape.

Input Specification:

Each input file contains one test case. Each case starts with a line containing two positive integers N (<=100), the number of crocodiles, and D, the maximum distance that James could jump. Then N lines follow, each containing the (x, y) location of a crocodile. Note that no two crocodiles are staying at the same position.

Output Specification:

For each test case, print in a line "Yes" if James can escape, or "No" if not.

Sample Input 1:
14 2025 -15-25 288 4929 15-35 -25 2827 -29-8 -28-20 -35-25 -20-13 29-30 15-35 4012 12
Sample Output 1:
Yes
Sample Input 2:
4 13-12 1212 12-12 -1212 -12
Sample Output 2:
No

这一题让我想起来我那段时间连续好几天把所有的007电影从头到尾刷了一遍……罪过……在这一集中,当时邦德被困在湖中心,鳄鱼从四周过来。正在邦德走投无路时,接下来奇迹发生了,所有的鳄鱼很自觉地排成了一条直线,刚好连着湖中小岛和岸边出口,然后邦德就这样出去了……根本不是题中所想像的那样左跳右跳……嗯……好了,不说废话了,来看一下题目吧!

这一题关键问题在于如何表示图。先来说说我拙劣的思维吧……=_=……湖是一个100×100的正方形,鳄鱼的坐标也都是整数,所以可以直接用矩阵来表示这一个湖啊~有鳄鱼的位置就标记为1,否则就标记为0。然后当邦德在某个位置时,就检查他的上下左右能跳到的范围内有没有鳄鱼,有的话就跳上去。当然有一些细节要注意:湖中小岛的直径是15,注意是直径,不是半径,半径是7.5;邦德在这种危急情况下有可能强烈分泌肾上腺素,于是可以不踩鳄鱼就一步跳到岸边(-_-);湖的坐标范围是从-50到50,而数组的下标是从0开始的,所以在我的方法中,需要把它“平移”到0到100范围中。

最初我用DFS方法,每跳一步就逐个检查上下左右每个格点中是否有鳄鱼。然后就超时……T_T……我觉得这种图的表示方法肯定不聪明,所以本来就想换一种方法。后来想想还是先试试BFS遍历吧,一试就通过了……原谅我这道题我就这样结束了。先来看一下我的代码吧:

#include <iostream>#include <queue>using namespace std;int n, d;int a[101][101] = {0};bool bondIsSafe = false;inline bool inRange( int x, int y, int i, int j ){if( (x-i)^2 + (y-j)^2 <= d^2 )return true;elsereturn false;}inline bool centerRange( int i, int j ){if( (i-50)^2 + (j-50)^2 <= (7.5+d)^2 )return true;elsereturn false;}int main(){cin >> n >> d;int x, y;for( int k=0; k<n; ++k ){cin >> x >> y;a[x+50][y+50] = 1;}queue<int> q;//------------------------for( int i=50-15/2-d; i<=50+15/2+d; ++i ){for( int j=50-15/2-d; j<=50+15/2+d; ++j ){if( i<=d || i>=100-d || j<=d || j>=100-d ){bondIsSafe = true;break;}if( a[i][j]==1 && centerRange(i,j) ){q.push(i); q.push(j);a[i][j] = 2;}while ( !q.empty() ){int xi=q.front();q.pop();int yi=q.front();q.pop();if( xi<=d || xi>=100-d || yi<=d || yi>=100-d ){bondIsSafe = true;break;}for( int x=xi-d; x<=xi+d; ++x ){for( int y=yi-d; y<=yi+d; ++y ){if( a[x][y]==1 ){a[x][y] = 2;q.push(x);q.push(y);}}}}}}if( bondIsSafe )cout << "Yes";elsecout << "No";return 0;}

后来姥姥告诉我,她就是用的DFS方法,而且根本不用建图。我想了想终于明白了,原来还可以这么简单。果然自己还差得远呢……可能你也能想到是怎么做到的,不过我先在这里卖个关子,到下一次的 Hard Version 时我再介绍那个超级简单的方法 ^_^


05-3. 六度空间

时间限制
1500 ms
内存限制
65536 kB
代码长度限制
8000 B
判题程序
Standard

“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”如图6.4所示。


图6.4 六度空间示意图

“六度空间”理论虽然得到广泛的认同,并且正在得到越来越多的应用。但是数十年来,试图验证这个理论始终是许多社会学家努力追求的目标。然而由于历史的原因,这样的研究具有太大的局限性和困难。随着当代人的联络主要依赖于电话、短信、微信以及因特网上即时通信等工具,能够体现社交网络关系的一手数据已经逐渐使得“六度空间”理论的验证成为可能。

假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。

输入格式说明:

输入第1行给出两个正整数,分别表示社交网络图的结点数N (1<N<=104,表示人数)、边数M(<=33*N,表示社交关系数)。随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个结点的编号(节点从1到N编号)。

输出格式说明:

对每个结点输出与该结点距离不超过6的结点数占结点总数的百分比,精确到小数点后2位。每个结节点输出一行,格式为“结点编号:(空格)百分比%”。

样例输入与输出:

序号输入输出1
10 91 22 33 44 55 66 77 88 99 10
1: 70.00%2: 80.00%3: 90.00%4: 100.00%5: 100.00%6: 100.00%7: 100.00%8: 90.00%9: 80.00%10: 70.00%
2
10 81 22 33 44 55 66 77 89 10
1: 70.00%2: 80.00%3: 80.00%4: 80.00%5: 80.00%6: 80.00%7: 80.00%8: 70.00%9: 20.00%10: 20.00%
3
11 101 21 31 44 56 56 76 88 98 1010 11
1: 100.00%2: 90.91%3: 90.91%4: 100.00%5: 100.00%6: 100.00%7: 100.00%8: 100.00%9: 100.00%10: 100.00%11: 81.82%
4
2 11 2
1: 100.00%2: 100.00%

话说好久没看到中文的题目了,还是中文看起来亲切啊……T_T……这一题非常适合用BFS,不过实际上用DFS也是可以的。只要把遍历的深度记录下来就可以了。按照姥姥的方法,每遍历一圈,就加一层深度,直到深度至6为止。如何知道某一层遍历完了呢?姥姥的方法是,在每一层遍历时,都把那一层的最后一个元素记录下来,我们用 tail 表示。这样当我们下一次来遍历这一层时,当我们遍历到 tail 位置时,就可以知道这一层就遍历完了。如果代码还不会写,可以参考姥姥的伪代码,基本上照着那个写就能写出来。亲测有效~(我是不是透露了什么~)需要注意的细节是,结点的编号是从1开始的,而不是0;最后计算的百分比,是包括自己结点在内的。好了,下面上代码:

#include <iostream>#include <queue>using namespace std;int n,m;int* a;bool* v;int BFS( int i ){v[i] = true;int count = 1;int level = 0;int last  = i;int tail  = i;queue<int> q;q.push( i );while( !q.empty() ){i = q.front();q.pop();for( int j=0; j<n; ++j ){if( a[i*n+j]==1 && !v[j] ){v[j] = true;q.push( j );++count;tail = j;}}if( i==last ){++level;last = tail;}if( level==6 )break;}return count;}void SDS(){for( int i=0; i<n; ++i ){for( int j=0; j<n; ++j )v[j] = false;int count = BFS( i );if( i!=0 )cout << endl;cout << i+1 << ": ";cout.setf(ios::fixed);cout.precision(2);cout << 100.0*(count)/n << '%';}}int main(){cin >> n >> m;a = new int [n*n];v = new bool [n];for (int i = 0; i < n*n; ++i)a[i] = 0;int x,y;for (int i = 0; i < m; ++i){cin >> x >> y;--x;--y;a[x*n+y] = 1;a[y*n+x] = 1;}SDS();delete [] a;delete [] v;return 0;}

0 0
原创粉丝点击