计算机考研机试指南(八)——图论(畅通工程、还是畅通工程、最短路、more is better、Freckles、legal or not、确定比赛名次、产生冠军、最短路径问题)

机试指南 cha 5 图论

并查集

畅通工程

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <math.h>
 5 #include <string.h>
 6 
 7 using namespace std;
 8 /*
 9 并查集
10 畅通工程:所有顶点之间均可连通
11 time : 20+20+20
12 */
13 
14 int findroot(int v[],int a)
15 {
16     // 寻找根节点的过程中,如果不是根节点的直接孩子,则修改为其直接孩子
17     if (v[a] == -1)
18         return a;
19     else
20     {
21         int tmp = findroot(v,v[a]);
22         v[a] = tmp;
23         return tmp;
24     }
25 }
26 int main()
27 {
28     int m,n,x1,x2;
29     int i,l,r;
30     int v[1001] = {0}; // v[i] 代表第i个顶点对应的双亲节点
31     while (cin >> n >> m)
32     {
33         if (n == 0)
34             break;
35         for (int i = 1;i<=n;i++)
36             v[i] = -1;
37         for (i = 0 ;i < m;i++)
38         {
39             cin >> x1 >> x2;
40             // 根节点不同,即不在一个集合,则连通
41             l = findroot(v,x1);
42             r = findroot(v,x2);
43             if (l!=r)
44                 v[r] = l;
45 
46 
47         }
48         // 输入完了以后,相连的结点会在同一个集合中,计算有几个并查集,并查集-1即为新增路径条数
49         int count = 0 ;
50         for (i = 1;i<=n;i++)
51         {
52             if (v[i] == -1)
53                 count ++;
54         }
55         cout << count -1 << endl;
56 
57     }
58     return 0;
59 }

最小生成树

还是畅通工程

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <math.h>
 5 #include <string.h>
 6 #include <algorithm>
 7 
 8 using namespace std;
 9 /*
10 最小生成树
11 time :
12 */
13 int v[1010];
14 int findroot(int a)
15 {
16     if (v[a] == -1)
17         return a;
18     else
19     {
20         int tmp = findroot(v[a]);
21         // 非根节点a直接和根相连
22         v[a] = tmp;
23         return tmp;
24     }
25 }
26 struct node
27 {
28   int a;
29   int b;
30   int w;
31 };
32 bool cmp(const node &a,const node &b)
33 {
34     return a.w < b.w; // 从小到大
35 }
36 int main()
37 {
38     int n,a,b,w;
39     int i,l,r;
40     node node[5000],tmp;
41     int ans = 0;
42     while (cin >>n && n!=0)
43     {
44         ans = 0;
45         for (int i = 1;i<=n;i++)
46             v[i] = -1;
47         // 初始化node数组
48         for (i = 0;i< n*(n-1)/2 ;i++)
49         {
50             cin >> a >>b >> w;
51             node[i].a =a;
52             node[i].b =b;
53             node[i].w =w;
54         }
55         sort(node,node+n*(n-1)/2,cmp); // 不是n条边!
56         for (i = 0;i<n*(n-1)/2;i++)
57         {
58             tmp = node[i];
59             l = findroot(tmp.a);
60             r = findroot(tmp.b);
61             if (l!=r)
62             {
63                 v[r] = l; // 两个根节点集合合并
64                 ans += tmp.w;
65             }
66         }
67         cout << ans << endl;
68 
69     }
70 
71     return 0;
72 }

最短路径

最短路

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <math.h>
 5 #include <string.h>
 6 #include <algorithm>
 7 
 8 #define INFINITY 65515
 9 using namespace std;
10 /*
11 最短路径
12 time :
13 */
14 
15 int main()
16 {
17     int i,j,n,m;
18     int p[101][101];
19     while (cin >> n >> m && (n!=0 && m != 0))
20     {
21         for (i = 1;i<=n;i++)
22             for (j=1;j<=n;j++)
23         {
24             if (i == j)
25                 p[i][i] = 0;
26             else
27                 p[i][j] = INFINITY;
28         }
29         for (i = 0 ;i < m;i++)
30         {
31             int a,b,c;
32             cin >>  a>> b >> c;
33             p[a][b] = c;
34             p[b][a] = c;
35         }
36         for (int k = 1 ; k <= n; k++)
37         {
38             for ( i = 1;i<=n ; i++)
39                 for (j = 1 ;j<=n ; j++)
40                     if ( i!=k && j!= k && (p[i][k] + p[k][j] < p[i][j]))
41                     {
42                        p[i][j] = p[i][k] + p[k][j];
43                     }
44         }
45         cout << p[1][n] <<endl;
46     }
47 
48     return 0;
49 }

more is better

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <math.h>
 5 #include <string.h>
 6 #include <algorithm>
 7 
 8 using namespace std;
 9 // 求并查集的过程中在每个集合根节点中记录该集合的人数
10 
11 const int num = 100001;
12 int v[num]; // v[i] 为第i个节点的双亲节点
13 int sum[num];// sum[i]当且仅当v[i] == -1 时有效,数字代表该集合的人数
14 
15 int findroot(int a)
16 {
17     if (v[a] == -1)
18         return a;
19     else
20     {
21         int tmp = findroot(v[a]); // 递归得到根节点
22         v[a] = tmp;
23         return tmp;
24     }
25 }
26 int main()
27 {
28 
29     int n;
30     int a,b,i,r,l;
31     while ( cin>>n )
32     {
33         for (i = 1;i<= num ; i++) // 这个地方不能设为2*n,因为如果n很大时,2*n可能会超过num
34         {
35             v[i] = -1; // 只知道N对好朋友,但不知道男孩的数目,最多为一对儿两个不同的男孩
36             sum[i] = 1;
37         }
38         for (i = 1 ;i <= n ;i++)
39         {
40             cin >> a >> b;
41             l = findroot(a);
42             r = findroot(b);
43             if (l!=r)
44             {
45               v[l] = r;
46               sum[r] += sum[l] ;// 注意!合并集合时要把两个集合的sum值合并
47             }
48 
49         }
50         int max = 1;
51         for (i = 1;i<=num;i++)
52         {
53             if (v[i] == -1 && sum[i] > max)
54             {
55                 max = sum[i];
56             }
57         }
58         cout << max <<endl;
59     }
60 
61 
62     return 0;
63 }

Freckles

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <math.h>
 5 #include <string.h>
 6 #include <algorithm>
 7 
 8 using namespace std;
 9 const int num = 101;
10 int v[num]; // 点集合
11 struct edge
12 {
13     int v1,v2; // 每条边的两个端点均在v[num]中
14     float w;
15 }e[num*(num-1)/2]; // 边集合
16 
17 int findroot(int a)
18 {
19     if (v[a] == -1)
20         return a;
21     else
22     {
23         int tmp = findroot(v[a]); // 递归得到根节点
24         v[a] = tmp;
25         return tmp;
26     }
27 }
28 bool cmp(const edge &a,const edge &b)
29 {
30     return a.w < b.w;
31 }
32 int main()
33 {
34     int n,i,j,l,r;
35     float dot[num][2],a,b;
36     float ans;
37     while (cin>>n)
38     {
39         for (i=1;i<=n;i++)
40         {
41             v[i] = -1;
42             cin >> dot[i][0] >> dot[i][1] ;
43         }
44         // 初始化边edge数组,n个点有n(n-1)/2条边,进行二层循环
45         float ans;
46         int size = 1;
47         for (i = 1 ;i <= n ;i++)
48         {
49             for (j = i+1 ; j <= n ; j++)
50             {
51                 a = dot[i][0] - dot[j][0];
52                 b = dot[i][1] - dot[j][1];
53                 ans = sqrt(a*a + b*b);
54                 e[size].v1 = i;
55                 e[size].v2 = j;
56                 e[size++].w = ans;
57 
58             }
59         }
60         // sort(edge),合并并查集
61         sort(e+1,e+size,cmp);
62 //        for (i = 1 ;i<4;i++)
63 //            cout << e[i].w << endl;
64         ans = 0;
65         // 从小权值边开始选择,每次要判断这条边的两个端点是否在同一个集合中
66         for (i = 1;i<size;i++)
67         {
68             l = findroot(e[i].v1);
69             r = findroot(e[i].v2);
70             if (l!=r)
71             {
72                 v[l] = r;
73                ans += e[i].w;
74             }
75         }
76         printf("%.2f",ans);
77 
78     }
79     return 0;
80 }

拓扑排序

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <vector>
 5 #include <queue>
 6 using namespace std;
 7 struct edge
 8 {
 9     int nextnode;
10 };
11 queue<int> q;
12 vector<edge> vex[100];
13 int visited[100];
14 
15 void findrudu(int n)
16 {
17     // 找到入度为0的点(未在邻接表右侧链中出现的点),放到队列中
18     int i,a;
19     bool rudu[100];
20     for (i = 0;i<100;i++)
21         rudu[i] = true;
22         // 一个再细微不过的知识点,都有可能成为决定成败的事情!
23     for (i = 0;i<n;i++)
24     {
25         for (vector<edge>::iterator it = vex[i].begin() ; it != vex[i].end() ; it++)
26         {
27             a = (*it).nextnode;
28             rudu[a] = false;
29         }
30     }
31 
32     for (i = 0;i<n;i++)
33     {
34       //  cout << rudu[i] <<' ' << visited[i] << endl;
35         if (rudu[i]&&visited[i]){
36             q.push(i);
37         }
38     }
39 }
40 int main()
41 {
42     int n,m,i,a,b;
43     edge e;
44     vector<edge>::iterator it;
45     // i 为顶点 , vex[i][0]为与该顶点相邻接的第一条弧的弧头顶点,vex[i] 为一个向量,记录与该顶点邻接的所有弧的弧头顶点
46     while (cin >> n >> m)
47     {
48         if (m==0&&n==0)
49             break;
50         // 初始化vector
51         for (i = 0;i<n;i++)
52         {
53             vex[i].clear();
54             visited[i] = true;
55         }
56         // 初始化队列
57         while (!q.empty())
58             q.pop();
59 
60         for (i = 0;i<m;i++)
61         {
62             cin >> a >> b; // a->b为一条弧
63             e.nextnode = b;
64             vex[a].push_back(e);
65         }
66         findrudu(n);
67         while (!q.empty())
68         {
69             a = q.front();// 从队列中取出一个入度为0的结点
70             q.pop();
71             vex[a].erase(vex[a].begin(),vex[a].end());// 删除所有的弧
72             visited[a] = false;
73             findrudu(n);
74         }
75         // 如果队列为空时仍旧有顶点为true,则存在环
76         bool flag = true;
77         for (i = 0;i<n;i++ )
78             if (visited[i])
79             {
80                 flag = false;
81             }
82         if (flag)
83             cout << "YES" << endl;
84         else
85             cout << "NO" << endl;
86 
87 
88 
89     }
90     return 0 ;
91 }

太蠢的做法!结果超时了,完全可以第一次统计每个结点的入度,然后每次删除节点时对该入度数组进行– 操作,用bool真的太蠢了!而且这道题墨迹了一整个下午! 以后半小时内做不出来就研究答案!一道题目不要超过1个半小时!

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <vector>
 5 #include <queue>
 6 using namespace std;
 7 
 8 queue<int> q;
 9 vector<int> vex[100];
10 
11 
12 int main()
13 {
14     int n,m,i,a,b;
15     vector<int>::iterator it;
16     int degree[100];
17     // i 为顶点 , vex[i][0]为与该顶点相邻接的第一条弧的弧头顶点,vex[i] 为一个向量,记录与该顶点邻接的所有弧的弧头顶点
18     while (cin >> n >> m)
19     {
20         if (m==0&&n==0)
21             break;
22         // 初始化vector和degree数组
23         for (i = 0;i<n;i++)
24         {
25             vex[i].clear();
26             degree[i] = 0;
27         }
28         // 初始化队列
29         while (!q.empty())
30             q.pop();
31         for (i = 0;i<m;i++)
32         {
33             cin >> a >> b; // a->b为一条弧
34             vex[a].push_back(b);
35             // 增加b的入度
36             degree[b] ++;
37         }
38         for (i = 0;i<n;i++)
39         {
40             if (degree[i] == 0)
41                 q.push(i);
42         }
43         int c = 0; // 计数器,用来确定已经被删除的结点的个数,或者用bool数组,但bool数组还得遍历
44         while (!q.empty())
45         {
46             a = q.front();// 从队列中取出队首
47             q.pop(); // 删除队首
48             c ++;
49             for (it = vex[a].begin();it != vex[a].end() ; it++)
50             {
51                 // 将待删除节点的邻接点的入度--,如果该邻接点成为入度为0的点,入队
52                 degree[*it] -- ;
53                 if (degree[*it] == 0)
54                     q.push(*it);
55             }
56             vex[a].erase(vex[a].begin(),vex[a].end());// 删除所有的邻接顶点
57 
58         }
59 
60         if (c == n)
61             cout << "YES" << endl;
62         else
63             cout << "NO" << endl;
64 
65 
66 
67     }
68     return 0 ;
69 }
  • 另一种遍历vector的方法
  • for (int i = 0;i

确定比赛名次 30min

和上面那道题比起来,只需要每次pop()之前或之后输出结点数据。而且并不需要erase掉被选中结点的链表,因为我们看的是被push进queue的结点。

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <vector>
 5 #include <queue>
 6 using namespace std;
 7 
 8 vector<int> vex[501];
 9 struct cmp{
10     bool operator() (const int &a, const int& b ){
11         return a > b;
12     }
13 };
14 priority_queue<int, vector<int>, cmp > q;
15 int degree[501];
16 // 如何保证号小的先被选中?优先队列!
17 int main()
18 {
19   int n,m,i,a,b;
20   while (cin>>n>>m)
21   {
22       // 初始化
23       for (i=1;i<=n;i++)
24       {
25           vex[i].clear();
26           degree[i] = 0;
27       }
28       // queue 没有 clear()方法,故需要循环pop()数据
29       while (!q.empty())
30         q.pop();
31 
32       for (i = 0;i<m;i++)
33       {
34           cin >> a >> b;
35           vex[a].push_back(b);
36           degree[b]++;
37       }
38       for (i=1;i<=n;i++)
39         if (degree[i] == 0)
40           q.push(i);
41       bool flag = true;
42       while (!q.empty())
43       {
44           a = q.top();// 选取队尾元素
45           q.pop();
46           if (flag)
47           {
48               flag = false;
49               cout << a;
50           }else
51           cout << ' ' << a;
52           for (i=0;i<vex[a].size();i++)
53           {
54               b = vex[a][i];
55               if ((--degree[b])==0)
56                 q.push(b);
57           }
58       }
59       cout << endl;
60   }
61     return 0 ;
62 }

产生冠军

没调过,已经调了一个小时了,附带上正确答案

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <vector>
 5 #include <queue>
 6 #include <map>
 7 #include <string>
 8 using namespace std;
 9 
10 vector<int> vex[10000];
11 int degree[10000];
12 queue<int> q;
13 int main()
14 {
15     int i,n,count,a;
16     string s1,s2;
17     map<string,int> m; // 映射
18     map<string,int>::iterator it;
19     while (cin>>n && n!=0)
20     {
21         count =0;
22         for (i=0;i<1000;i++)
23         {
24             vex[i].clear();
25             degree[i] = 0;
26         }
27         for (i=0;i<n;i++)
28         {
29             // 难点:把名字和数组的i进行映射,不知道具体的队员个数
30             cin >>s1>>s2;
31             if (m.find(s1) == m.end())
32                 m.insert(pair<string,int>(s1,count++));
33             if (m.count(s2)==0)
34                 m.insert(pair<string,int>(s2,count++));
35             vex[m[s1]].push_back(m[s2]);
36             degree[m[s2]]++;
37             // 如果插入的字符串已经出现过了则不会执行该条语句的啦!名字和索引的映射成功!
38 //            for (it = m.begin() ; it!=m.end();it++)
39 //                cout << (*it).first << " : " << (*it).second << endl;
40         }
41         // !!!!!!
42         m.clear();
43         while (!q.empty())
44             q.pop();
45 
46         for (i=0;i<m.size();i++)
47             if (degree[i] == 0)
48                 q.push(i);
49         int cnt = 0;
50         while (!q.empty())
51         {
52             a = q.front();
53             q.pop();
54             cnt ++;
55             for (i=0;i<vex[a].size();i++)
56             {
57                 if ((--degree[vex[a][i]])==0)
58                     q.push(vex[a][i]);
59             }
60         }
61         if (cnt == 1)
62             cout << "Yes" << endl;
63         else
64             cout << "No" << endl;
65     }
66     return 0 ;
67 }

正确答案:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <string>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <queue>
 7 #include <iostream>
 8 #include <map>
 9 using namespace std;
10 map<string,int> name;
11 vector<int> edge[10000];
12 queue<int> Q;
13 int indegree[10000];
14 int main(){
15     int n,i,j;
16     while(~scanf("%d",&n)&&n){
17         for(i=0;i<10000;i++) { edge[i].clear();indegree[i]=0;}
18         while(!Q.empty()) Q.pop();
19         i=0;
20         name.clear();
21         for(j=0;j<n;j++){
22             string a,b;
23             cin>>a>>b;
24             if(name.find(a)==name.end()) { name[a]=i++;}
25             if(name.find(b)==name.end())  { name[b]=i++;}
26             edge[name[a]].push_back(name[b]);
27             indegree[name[b]]++;
28         }
29         int num=i;
30         int cnt=0;
31         for(i=0;i<num;i++)
32             if(indegree[i]==0)    cnt++;
33         /*
34         int num=i;
35         for(i=0;i<n;i++){
36             if(indegree[i]==0) Q.push(i);
37         }
38         int cnt=0;
39         while(!Q.empty()){
40             int nowp=Q.front();
41             Q.pop();
42             cnt++;
43             for(i=0;i<edge[nowp].size();i++){
44                 indegree[edge[nowp][i]]--;
45                 if(indegree[edge[nowp][i]]==0) Q.push(edge[nowp][i]);
46             }
47         }
48         */
49         if(cnt==1) puts("Yes");
50         else puts("No");
51     }
52     return 0;
53 }

最短路径问题

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <vector>
 5 #include <queue>
 6 #include <map>
 7 #include <string>
 8 #define INFINITY 65535
 9 using namespace std;
10 
11 struct edge
12 {
13     int nextnode;
14     int d;//距离
15     int p;//花费
16 };
17 vector<edge> vex[1001]; // 邻接链表,vex[i]为一个向量,记录与vex[i]邻接的所有边的信息(邻接点以及权值)
18 bool S[1001]; // s集合为已经找到最短路径的顶点集合
19 int dis[1001]; // 距离向量
20 int cost[1001];//花费向量
21 int main()
22 {
23     int n,m,d,p,s,t,i,j,k,min,a,b;
24     edge e;
25     while (cin >> n >> m && n!=0 && m!=0)
26     {
27         // 1. 初始化邻接链表、距离向量和集合S,现在未知初始顶点和结束顶点是什么
28         for (i=1;i<=n;i++)
29         {
30             vex[i].clear();
31             S[i] = false;
32             dis[i] = INFINITY; // -1代表不可达
33             cost[i] = 0;
34         }
35         // 2.输入数据
36         for (i=0;i<m;i++)
37         {
38             cin >> a>>b>>d>>p;
39             e.d = d;e.p = p;
40             e.nextnode = b;
41             vex[a].push_back(e);
42             e.nextnode = a;
43             vex[b].push_back(e);
44             // 无向图,加入两条边
45         }
46         cin >> s >>t ;
47         // 确定初始顶点后,初始化集合S和dis
48         S[s] = true;dis[s] = 0; // 初始顶点自己到自己为0
49         for (i=0;i<vex[s].size();i++)
50         {
51             int num = vex[s][i].nextnode;
52             dis[num] = vex[s][i].d;
53             cost[num] = vex[s][i].p;
54         }
55         // 循环找到最短路径
56         for (i=2;i<=n;i++)
57         {
58             // 找到当前最短路径和顶点
59             min = INFINITY;
60             for (j=1;j<=n;j++)
61             {
62                 if (!S[j] && min > dis[j] )
63                 {
64                     k = j;
65                     min = dis[j];
66                 }
67             }
68             S[k] = true;
69 
70             for (j = 0;j<vex[k].size();j++)
71             {
72                 int node = vex[k][j].nextnode;
73                 if (S[node])
74                     continue;
75 
76                 if (min+vex[k][j].d<dis[node] || min+vex[k][j].d == dis[node] && cost[node] > cost[k]+vex[k][j].p)
77                 {
78                     dis[node] = min + vex[k][j].d;
79                     cost[node] = cost[k]+vex[k][j].p;
80                 }
81             }
82 
83         }
84         cout << dis[t]<<" "<< cost[t]<< endl;
85 
86     }
87 
88     return 0 ;
89 }

猜你喜欢

转载自www.cnblogs.com/twomeng/p/9509663.html