[关闭]
@sensitive-cs 2017-03-29T13:11:47.000000Z 字数 5290 阅读 1311

CQUPT 拓扑排序

题解


hdu2647 reward
题意:
有一个老板,他有n个员工,在发工资的时候,他的有些员工有要求,比如说a要求自己的工资要比b高。现在有m个要求,老板想用最少的钱去付工资。如果都能满足这些要求的话,输出最少的钱,否则输出-1.
思路:
拓扑排序,反向建图,(正向建图无法做)。最先我是用的双层循环,wa几发,时间复杂度是n^2,应该是t了,hdu好奇怪(可能是我本来就错了呢?
后来用队列来做,一次就过,以后都用队列了,实现起来也简单。
代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <vector>
  4. #include <queue>
  5. using namespace std;
  6. int indr[10005];
  7. int fee[10005];
  8. vector<int> v[10005];
  9. queue<int> qq;
  10. int main()
  11. {
  12. int n,m;
  13. while (scanf("%d%d",&n,&m) != EOF)
  14. {
  15. while (!qq.empty()) qq.pop();
  16. memset(indr,0,sizeof(indr));
  17. memset(fee,0,sizeof(fee));
  18. memset(v,0,sizeof(v));
  19. for (int i = 1;i <= m;i++)
  20. {
  21. int x,y;
  22. scanf("%d%d",&x,&y);
  23. indr[x]++;
  24. v[y].push_back(x);
  25. }
  26. int flag = 0;
  27. for (int i = 1;i <= n;i++)
  28. if (indr[i] == 0)
  29. {
  30. qq.push(i);
  31. }
  32. while (!qq.empty())
  33. {
  34. int mark = qq.front();
  35. qq.pop();
  36. for (int i = 0;i < v[mark].size();i++)
  37. {
  38. indr[v[mark][i]]--;
  39. fee[v[mark][i]] = fee[mark] + 1;
  40. if (indr[v[mark][i]] == 0) qq.push(v[mark][i]);
  41. }
  42. }
  43. for (int i = 1;i <= n;i++)
  44. if (indr[i] != 0) flag = 1;
  45. if (flag) printf("-1\n");
  46. else
  47. {
  48. long long sum = 0;
  49. for (int i = 1;i <= n;i++)
  50. sum += (fee[i]+888);
  51. printf("%lld\n",sum);
  52. }
  53. }
  54. return 0;
  55. }

poj1386 play on words
题意:
输入是n行字符串,用相当于词语接龙的方法,上一个字符串的最后一个字符与后一个字符串的第一个字符相同,用完所有的字符,问是否可能。
思路:
如果把字母当成节点,单词当成边的话,那么我们的任务就是找到一条欧拉路或者欧拉回路。(如何想到的?。。。)
用并查集判断图的联通很方便。
算法:
存在欧拉(回)路的条件:
1底图联通
2欧拉路:只存在两个节点的度为奇数,其余为偶数,且度数为奇数的节点中,一个入度比出度大1,一个出度比入度大1.
3欧拉回路:所有的点的度均为偶数。
代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. int vis[26];
  4. int g[26][26];
  5. int iv[26];
  6. int ov[26];
  7. int res = 0;
  8. void dfs(int v)
  9. {
  10. vis[v] = 1;
  11. res++;
  12. for (int i = 0;i < 26;i++)
  13. {
  14. if (g[v][i] && !vis[i])
  15. dfs(i);
  16. }
  17. }
  18. int main()
  19. {
  20. int t;
  21. scanf("%d",&t);
  22. while (t--)
  23. {
  24. int n;
  25. scanf("%d",&n);
  26. memset(vis,0,sizeof(vis));
  27. memset(g,0,sizeof(g));
  28. memset(iv,0,sizeof(iv));
  29. memset(ov,0,sizeof(ov));
  30. char s[1005];
  31. for (int i = 0;i < n;i++)
  32. {
  33. res = 0;
  34. scanf("%s",s);
  35. int len = strlen(s) - 1;
  36. int in = s[len] - 'a';
  37. int out = s[0] - 'a';
  38. g[in][out] = g[out][in] = 1;
  39. iv[in]++;
  40. ov[out]++;
  41. }
  42. int num = 0,st = 0,en = 0,word = 0,mark;
  43. for (int i = 0;i < 26;i++)
  44. {
  45. if (iv[i] || ov[i])
  46. {
  47. word++;
  48. mark = i;
  49. }
  50. if (iv[i] && ov[i] && iv[i] == ov[i]) num++;
  51. if (iv[i] - ov[i] == 1) en++;
  52. if (ov[i] - iv[i] == 1) st++;
  53. }
  54. bool flag = 0;
  55. if (num == word) flag = 1;
  56. else if (num == word-2 && en == 1 && st == 1) flag = 1;
  57. dfs(mark);
  58. if (res != word) flag = 0;
  59. if (flag) printf("Ordering is possible.\n");
  60. else printf("The door cannot be opened.\n");
  61. }
  62. return 0;
  63. }

hdu1811:rank of tetris
题意:中文,略
思路:
一开始,就想到用拓扑排序,但是对于冲突并没有完全理解,以为只要按照关系可以写出拓扑序就不是矛盾,但是是错误的。
比如第二个样例
1 = 2
1 > 3
2 > 0
0 > 1
2 > 0 与 0 > 1 推出的是2 > 1 与第一个条件矛盾了,这样也称为矛盾。
后来看了题解才知道,可以用并查集来表示相等的关系。为什么可以这样做呢?当我们用根来进行了正确的拓扑排序之后,一个集合中的元素因为有rp的高低,所以也能进行正确的排序,这样就证明了用并查集表示的合理性。
其次,我以前用拓扑排序判断环,是进行了排序之后检查所有的点的入度是否都为0,有不为0的就判断有环,其实这样做是有漏洞的(虽然不知道是什么)。有一种更好的判断环的方法,就是确定进行拓扑排序的点的总数是否与点的总数相等,这样非常的方便,因为我在进行拓扑排序的时候就可以记录点数。第二点呢,就是判断是否有多个拓扑序存在,即当队列中超过一个点时,就说明入度为0的点有多个,就证明了有多个拓扑序。
还有就是每次写并查集一定要写init函数啊,不要忘记了。
代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <vector>
  4. #include <queue>
  5. using namespace std;
  6. vector<int> v[10005];
  7. queue<int> mmp;
  8. int in[10005];
  9. int par[10005];
  10. struct node
  11. {
  12. int p,q;
  13. char op;
  14. };
  15. typedef struct node node;
  16. node rate[10005];
  17. int fin(int x)
  18. {
  19. if (par[x] == x)
  20. return x;
  21. else
  22. return par[x] = fin(par[x]);
  23. }
  24. bool unit(int x,int y)
  25. {
  26. x = fin(x);
  27. y = fin(y);
  28. if (x != y)
  29. {
  30. par[x] = y;
  31. return 1;
  32. }
  33. return 0;
  34. }
  35. void init(void)
  36. {
  37. for (int i = 0;i < 10005;i++)
  38. par[i] = i;
  39. }
  40. int main()
  41. {
  42. int n,m;
  43. while (scanf("%d%d",&n,&m) != EOF)
  44. {
  45. int sum = n;
  46. bool con = 0,unc = 0;
  47. memset(rate,0,sizeof(rate));
  48. memset(v,0,sizeof(v));
  49. memset(in,0,sizeof(in));
  50. memset(par,0,sizeof(par));
  51. while (!mmp.empty()) mmp.pop();
  52. init();
  53. for (int i = 1;i <= m;i++)
  54. {
  55. scanf("%d %c %d",&rate[i].p,&rate[i].op,&rate[i].q);
  56. if (rate[i].op == '=')
  57. {
  58. if (unit(rate[i].p,rate[i].q))
  59. --sum;
  60. }
  61. }
  62. for (int i = 1;i <= m;i++)
  63. {
  64. int tx = rate[i].p,ty = rate[i].q;
  65. tx = fin(tx);
  66. ty = fin(ty);
  67. if (rate[i].op == '>')
  68. {
  69. in[tx]++;
  70. v[ty].push_back(tx);
  71. }
  72. else if (rate[i].op == '<')
  73. {
  74. in[ty]++;
  75. v[tx].push_back(ty);
  76. }
  77. }
  78. for (int i = 0;i < n;i++)
  79. {
  80. if (i == fin(i) && in[i] == 0)
  81. mmp.push(i);
  82. }
  83. while (!mmp.empty())
  84. {
  85. if (mmp.size() > 1) unc = 1;
  86. int tmp = mmp.front();
  87. mmp.pop();
  88. for (int j = 0;j < v[tmp].size();j++)
  89. {
  90. int tp = v[tmp][j];
  91. in[tp]--;
  92. if (!in[tp]) mmp.push(tp);
  93. }
  94. --sum;
  95. }
  96. if (sum > 0) con = 1;
  97. if (con)
  98. {
  99. printf("CONFLICT\n");
  100. continue;
  101. }
  102. if (unc)
  103. {
  104. printf("UNCERTAIN\n");
  105. continue;
  106. }
  107. printf("OK\n");
  108. }
  109. return 0;
  110. }

HDU5883:The best path
题意:
有n个湖,m条河,每个湖有一个值,找一条一笔画可以经过每一条河一次,且找出这条路线的每个湖的值的异或值最大。
思路:
一笔画,欧拉路,然而这并不是重点。
坑:
这才是重点!!!!!!!!!!!!
首先,这是一个无向图,然而我把它当成了有向图去处理。
第二,这些湖可能有些湖是孤立的,因为河水并不一定要连到湖。。。想的吐血了。。。
第三,就是孤立的湖,我们不能计算它的贡献。。。。
新姿势:
如果一个数异或偶数次,那么结果是0
0异或任何数都是这个数本身。
如果一个数列是确定的,那么异或的顺序不影响结果。
难点:
对于欧拉通路,起点和终点的贡献加一次,所以判断每个点是否有贡献,我们可以用(d[i]+1)/2%2是否为1进行判断,举个例子,假如起点的度是7次,那么就是经过3次,还有1次(孤立的),所以一共4次,实际没有贡献,用上述的公式算出来的就是8/2%2就是0;再如一个终点的度数为5,那么就是经过了完整的两次加上1次,共3次,所以有贡献,上述算出来也是3.对于偶数度的点,不会有丝毫的影响,1在/的时候就已经gg了。
新得:
不知道女装做题是否可以减少bug啊。
代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <algorithm>
  4. using namespace std;
  5. int du[100005];
  6. int va[100005];
  7. int par[100005];
  8. void init(int n)
  9. {
  10. for (int i = 0;i <= n;i++)
  11. par[i] = i;
  12. }
  13. int fin(int x)
  14. {
  15. if (x == par[x])
  16. return x;
  17. else
  18. return par[x] = fin(par[x]);
  19. }
  20. void unit(int x,int y)
  21. {
  22. x = fin(x);
  23. y = fin(y);
  24. if (x != y)
  25. par[x] = y;
  26. }
  27. int main()
  28. {
  29. int t;
  30. scanf("%d",&t);
  31. while (t--)
  32. {
  33. int n,m;
  34. scanf("%d%d",&n,&m);
  35. memset(du,0,sizeof(du));
  36. memset(va,0,sizeof(va));
  37. memset(par,0,sizeof(par));
  38. init(n);
  39. for (int i = 1;i <= n;i++)
  40. {
  41. scanf("%d",&va[i]);
  42. }
  43. for (int i = 1;i <= m;i++)
  44. {
  45. int x,y;
  46. scanf("%d%d",&x,&y);
  47. unit(x,y);
  48. du[x]++;
  49. du[y]++;
  50. }
  51. int fa;
  52. for (int i = 1;i <= n;i++)
  53. {
  54. if (du[i]) fa = i;
  55. }
  56. fa = fin(fa);
  57. int flag = 1;
  58. for (int i = 1;i <= n;i++)
  59. {
  60. if (du[i] && fa != fin(i)) flag = 0;
  61. }
  62. if (!flag)
  63. {
  64. //printf("@\n");
  65. printf("Impossible\n");
  66. continue;
  67. }
  68. int odd = 0;
  69. for (int i = 1;i <= n;i++)
  70. {
  71. if (du[i] % 2) odd++;
  72. }
  73. bool tl = 0,hl = 0;
  74. if (odd == 0) hl = 1;
  75. else if (odd == 2) tl = 1;
  76. else flag = 0;
  77. if (!flag)
  78. {
  79. printf("Impossible\n");
  80. continue;
  81. }
  82. int ans = 0;
  83. for (int i = 1;i <= n;i++)
  84. {
  85. if ((du[i]+1) / 2 % 2) ans ^= va[i];
  86. }
  87. int tmp = ans;
  88. if (hl)
  89. {
  90. for (int i = 1;i <= n;i++)
  91. if (du[i])
  92. ans = max(ans,tmp^va[i]);
  93. }
  94. printf("%d\n",ans);
  95. }
  96. return 0;
  97. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注