zoukankan      html  css  js  c++  java
  • 计算机考研机试指南(八)——图论(畅通工程、还是畅通工程、最短路、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 }
  • 相关阅读:
    dhl:asp.net mvc 在View中获取Url参数的值
    Sql Server 中序号列的实现方法
    在LINQ中實踐多條件LEFT JOIN
    css div ul li 导航栏(横向):(纯CSS 多级菜单IE6能支持的)
    学习JQuery的$.Ready()与OnLoad $(window).load() 方法事件比较
    dhl:juery提示插件:jquery.Popup.js
    javascript中0级DOM和2级DOM事件模型浅析
    mvc:尽可能摆脱对HttpContext的依赖
    [备]ASP.net 中 OutputCache 指令各个参数的作用。
    dhl:我的jQuery代码:续...
  • 原文地址:https://www.cnblogs.com/twomeng/p/9509663.html
Copyright © 2011-2022 走看看