[关闭]
@morehigh 2017-02-25T11:15:44.000000Z 字数 3649 阅读 973

CQUPT 集训队专题训练(拓扑排序/欧拉路)

拓扑排序/欧拉路


A - 确定比赛名次
题意:

  1. 输入有若干组,每组中的第一行为二个数N1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1P2表示即P1队赢了P2队。给出一个符合要求的排名。

解题思路:

  1. 拓扑排序:首先要找到任意入度为0的一个顶点,删除它及所有相邻的边,再找入度为0的顶点,以此类推,直到删除所有顶点。将入度为0的点一一保存在数组中。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. using namespace std;
  7. int a[510][510],b[510],ans[510];
  8. int main()
  9. {
  10. int n,m,p1,p2;
  11. while(scanf("%d%d",&n,&m)!=EOF)
  12. {
  13. memset(a,0,sizeof(a));
  14. memset(b,0,sizeof(b));
  15. for(int i=1;i<=m;i++)
  16. {
  17. scanf("%d%d",&p1,&p2);
  18. if(a[p1][p2]==0)
  19. b[p2]++;
  20. a[p1][p2]=1;
  21. }
  22. int num=0;
  23. int t;
  24. for(int i=0;i<n;i++)
  25. {
  26. for(int j=1;j<=n;j++)
  27. {
  28. if(b[j]==0)
  29. {
  30. t=j;
  31. ans[num++]=t;
  32. b[j]--;
  33. break;
  34. }
  35. }
  36. for(int j=1;j<=n;j++)
  37. {
  38. if(a[t][j]==1)
  39. b[j]--;
  40. }
  41. }
  42. for(int i=0;i<n-1;i++)
  43. printf("%d ",ans[i]);
  44. printf("%d\n",ans[n-1]);
  45. }
  46. return 0;
  47. }

B - Reward
题意:

  1. n个工人,m个要求(n<=10000,m<=20000),每个要求输出a,b表示a的奖金要比b的高,求出满足所有要求后,老板所要发的奖金的最少。

解题思路:

  1. 由于发的奖金的金额是按照梯度进行递增的,需要拓扑排序排在后面的奖金要比其相邻的前面的奖金要多1,才能满足老板发的奖金最少,用前向星维护相连的边,用队列来维护入度为0的顶点,再出队列时,用sum求奖金和。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. #include<queue>
  7. #define maxx 10005
  8. using namespace std;
  9. int n,sum,ans;
  10. int in[maxx],head[maxx],mon[maxx];
  11. struct Node
  12. {
  13. int to;
  14. int next;
  15. }edge[2*maxx];
  16. void solve()
  17. {
  18. int v;
  19. queue<int>Q;
  20. for(int i=1;i<=n;i++)
  21. if(in[i]==0)
  22. Q.push(i);
  23. while(!Q.empty())
  24. {
  25. v=Q.front();
  26. sum+=mon[v];
  27. Q.pop();
  28. ans++;
  29. for(int i=head[v];i!=-1;i=edge[i].next)
  30. {
  31. if(--in[edge[i].to]==0)
  32. Q.push(edge[i].to);
  33. mon[edge[i].to]=mon[v]+1;
  34. }
  35. }
  36. }
  37. int main()
  38. {
  39. int m,a,b,tot;
  40. while(scanf("%d%d",&n,&m)!=EOF)
  41. {
  42. memset(head,-1,sizeof(head));
  43. memset(in,0,sizeof(in));
  44. for(int i=1;i<=n;i++)
  45. mon[i]=888;
  46. tot=0;
  47. sum=0;
  48. ans=0;
  49. while(m--)
  50. {
  51. scanf("%d%d",&a,&b);
  52. edge[tot].to=a;
  53. edge[tot].next=head[b];
  54. head[b]=tot++;
  55. in[a]++;
  56. }
  57. // for(int i=1;i<=n;i++)
  58. // cout<<head[i]<<endl;
  59. solve();
  60. if(ans!=n)
  61. sum=-1;
  62. cout<<sum<<endl;
  63. }
  64. return 0;
  65. }

D - 欧拉回路
题意:

  1. 欧拉回路是指不令笔离开纸面,可画过图中每条边仅一次,且可以回到起点的一条回路。现给定一个图,问是否存在欧拉回路?

解题思路:

  1. 欧拉回路必须保证这条路联通,用并查集判断图的连通性,然后图中所有节点度均为偶数。

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. using namespace std;
  7. int in[1005],fa[1005];
  8. int find(int x)
  9. {
  10. return fa[x]==x?x:fa[x]=find(fa[x]);
  11. }
  12. void unite(int a,int b)
  13. {
  14. int aa=find(a);
  15. int bb=find(b);
  16. if(aa!=bb)
  17. {
  18. fa[bb]=aa;
  19. }
  20. }
  21. int main()
  22. {
  23. int n,m;
  24. int a,b;
  25. while(scanf("%d",&n)!=EOF&&n)
  26. {
  27. memset(in,0,sizeof(in));
  28. for(int i=1;i<=n;i++)
  29. fa[i]=i;
  30. scanf("%d",&m);
  31. for(int i=0;i<m;i++)
  32. {
  33. scanf("%d%d",&a,&b);
  34. unite(a,b);
  35. in[a]++;
  36. in[b]++;
  37. }
  38. int flag=1;
  39. int temp=fa[1];
  40. for(int i=1;i<=n;i++)
  41. {
  42. if(fa[i]!=temp)
  43. flag=0;
  44. if(in[i]%2!=0)
  45. flag=0;
  46. }
  47. cout<<flag<<endl;
  48. //
  49. }
  50. return 0;
  51. }

E - Play on Words
题意:

  1. 给出N个单词(1 <= N <= 100000),判断是否满足一种排序,使得前一个单词的最后一个字母与后一个单词的第一个字母相同。

解题思路:

  1. 将每个单词的首尾当作图的顶点,判断是否为欧拉通路:图连通;除2个端点外其余节点入度=出度;1个端点入度比出度大1;一个端点入度比出度小1。用并查集判断图是否联通,记录出度入度情况,最后判断是否为欧拉通路

代码:

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstring>
  4. #include <cmath>
  5. #include <algorithm>
  6. using namespace std;
  7. int n;
  8. int fa[30],rank[30];
  9. int in[30],out[30];
  10. bool ex[30];
  11. int find(int x)
  12. {
  13. return x==fa[x]?x:fa[x]=find(fa[x]);
  14. }
  15. void Union(int x,int y)
  16. {
  17. int xx=find(x);
  18. int yy=find(y);
  19. if(xx!=yy)
  20. {
  21. /* if(rank[xx]>rank[yy])
  22. fa[yy]=xx;
  23. else
  24. {
  25. if(rank[xx]==rank[yy])
  26. rank[yy]++;
  27. fa[xx]=yy;
  28. }
  29. */
  30. fa[yy]=xx;
  31. }
  32. }
  33. int main()
  34. {
  35. int t;
  36. char word[1024];
  37. cin>>t;
  38. while(t--)
  39. {
  40. scanf("%d",&n);
  41. for(int i=0;i<26;i++)
  42. {
  43. in[i]=out[i]=0;
  44. fa[i]=i;
  45. // rank[i]=0;
  46. ex[i]=false;
  47. }
  48. for(int i=0;i<n;i++)
  49. {
  50. scanf("%s",word);
  51. int w1=word[0]-'a';
  52. int w2=word[strlen(word)-1]-'a';
  53. in[w1]++;
  54. out[w2]++;
  55. ex[w1]=ex[w2]=true;
  56. Union(w1,w2);
  57. }
  58. int ans=0;
  59. for(int i=0;i<26;i++)
  60. {
  61. if(ex[i]&&i==fa[i])
  62. ans++;
  63. if(ans>1)
  64. break;
  65. }
  66. if(ans>1)
  67. {
  68. printf("The door cannot be opened.\n");
  69. continue;
  70. }
  71. int flag=0;
  72. int s1=0,s2=0;
  73. for(int i=0;i<26;i++)
  74. {
  75. if(ex[i]&&in[i]!=out[i])
  76. {
  77. if(in[i]==out[i]+1)
  78. s1++;
  79. else if(in[i]+1==out[i])
  80. s2++;
  81. else
  82. {
  83. flag=1;
  84. break;
  85. }
  86. }
  87. }
  88. if(flag)
  89. {
  90. printf("The door cannot be opened.\n");
  91. }else
  92. {
  93. if(s1<=1&&s2<=1)
  94. printf("Ordering is possible.\n");
  95. else
  96. printf("The door cannot be opened.\n");
  97. }
  98. }
  99. return 0;
  100. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注