当前位置:   article > 正文

Codeforces Round #508 (Div. 2)_codeforce 508

codeforce 508

A. Equality

给定一个字符串,求最长子字符串,要求所有字符出现次数相同。注意,由于要求使用的字符数k已经给出,但是不一定全部出现在字符串里,所以可能只能构造空字符串。

很简单了,先将所有字符的出现次数统计出来。然后看k个字符出现次数最少的那个,然后将最小出现次数乘以k就行了。

  1. [n,k] = map(int, input().split())
  2. const = input()
  3. counting = {}
  4. for i in range(k):
  5. counting[chr(ord('A') + i)] = 0
  6. for i in const:
  7. counting[i] += 1
  8. ret = n
  9. for i,v in counting.items():
  10. ret = min(ret, v)
  11. print(ret*k)

 B. Non-Coprime Partition

给出n,将从1到n这n个数字分到两份,分别求和得到sum1和sum2,求gcd(sum1,sum2) > 1的的分割方法。打印是否可以分割,然后把分割打印出来。

这里有一点小技巧。很明显,对于n为1,2,都是没有结果的,但是对于大于等于3的n,都是有结果的。但是具体要怎么分呢?我们直到n个数字的和为 (1 + n ) * n /2。这里,和分两种。奇数,偶数。

1,对于和为偶数的情况,任何偶数减2都还是偶数。所以最简单的分配方法,就是2一波,剩下的一波。他们的gcd为2。

2,对于和为奇数的情况,需要考虑两种情况,

a,n为奇数,这个时候,中间数middle = (1 + n)/2,必定有middle * n = (n + 1) * n /2 。很明显了,这个时候,middle一波,剩下的数一波。gcd为middle=(n+1)/2

b,n为偶数,这个时候,(1+n)/2 = middle,但是这个middle不是整数。所以我们换一个middle,用n/2。同理,middle一波,剩下的数一波,gcd为middle=n/2

  1. n = int(input())
  2. if n <= 2:
  3. print("No")
  4. else:
  5. print("Yes")
  6. sum = (n + 1) * n / 2
  7. if(sum % 2 == 0):
  8. print("1 2")
  9. anotherLine = "%d 1" % (n - 1)
  10. for i in range(3,n + 1):
  11. anotherLine += " %d" % i
  12. print(anotherLine)
  13. else:
  14. if n % 2 == 0:
  15. middle = n /2
  16. else:
  17. middle = (n + 1) / 2
  18. print("1 %d" % (middle))
  19. anotherLine = "%d 1" % (n - 1)
  20. for i in range(2,n + 1):
  21. if i != middle:
  22. anotherLine += " %d" % i
  23. print(anotherLine)

C. Gambling

A,B两人分别有一组数字,没人可以从自己的数组取一个数字,拿起来。或者从对手的数组取一个数字,移出,不放入任何人的口袋。两人数组清空之后,游戏结束。游戏结束后,两人手中分别求手上的数字之和。求两人在用最优策略时,两人的数字相差为多少。

这个策略用贪心即可。把两人手头的数字从大到小排序,每一轮,如果自己手头的最大的数字比对手大,就拿起来,否则,移除对手的数字。

嗯。。。就是我这代码晚上写的,不够简洁。。。。

  1. n = int(input())
  2. A = list(map(int, input().split()))
  3. B = list(map(int, input().split()))
  4. A = sorted(A,reverse = True)
  5. B = sorted(B,reverse = True)
  6. sA, sB = 0, 0
  7. i,j = 0,0
  8. turn = 1
  9. while True:
  10. turn += 1
  11. turn %= 2
  12. if i >= n and j >= n:
  13. break;
  14. if i == n:
  15. if turn == 1:
  16. #turn for b and a is list empty
  17. sB += B[j]
  18. j+=1
  19. continue
  20. if j == n:
  21. if turn == 0:
  22. #turn for a and b is list empty
  23. sA += A[i]
  24. i+=1
  25. continue
  26. if turn == 0:
  27. #turn for a
  28. if A[i] > B[j]:
  29. sA += A[i]
  30. i +=1
  31. elif A[i] < B[j]:
  32. j +=1
  33. else:
  34. j +=1
  35. else:
  36. if A[i] > B[j]:
  37. i += 1
  38. elif A[i] < B[j]:
  39. sB += B[j]
  40. j += 1
  41. else:
  42. i += 1
  43. print(sA-sB)

D. Slime

有一排史莱姆,他们可以吃自己左边或者右边的史莱姆。每个史莱姆有一个数值。每次吞噬,都用自己的数字减对方的数字。求所有史莱姆吞噬完,只剩一个史莱姆的时候,能得到的最大数字是多少。

这个策略也简单。

如果输入有正有负,则将所有数字的绝对值加起来,即可(很简单,从负数开始,一直用小的吃大的,不断积累,最后用正数吃负数即可)

如果输入全部为正或者全部为负,则将所有数字的绝对值加起来,然后减 2 乘以最小的绝对值即可。第一步用最小的绝对值吃旁边的史莱姆(或者被吃掉),得到一个有正有负的数列。然后重复上面有正有负的步骤。

  1. import sys
  2. n = int(input())
  3. line = list(map(int, input().split()))
  4. if n == 1:
  5. print(line[0])
  6. sys.exit(0)
  7. sum = 0
  8. minNum = 10 ** 9
  9. maxNum = -10**9
  10. for i in line:
  11. if minNum > i:
  12. minNum = i
  13. if maxNum < i:
  14. maxNum = i
  15. sum += abs(i)
  16. if minNum * maxNum < 0:
  17. print(sum)
  18. elif minNum < 0:
  19. print(sum - 2 * abs(maxNum))
  20. else:
  21. print(sum - 2 * abs(minNum))

E. Maximum Matching

有很多block,block左右分别有一种颜色,左右的,从所有block里面选一部分出来,同颜色的边可以拼在一起。求拼在一起的block的value的和最大为多少。

我一开始以为是动态规划,折腾了很久。后来看了别人的答案才知道,嗯。。。欧拉图。。。

我们把四个颜色看成四个点,一个block表示从一个点到另一个点的一条边。block的value就是边的权重。求一个子图,该子图构成欧拉回路,并且权重之和最大。

进一步,由于只有四个点,又是无向图,所以我们可以有这样的结论,要么刚好所有节点都是偶数度节点,要么有两个节点是奇数度节点,要么四个节点都是奇数度节点。这里其实只有最后一种情况不符合欧拉图的要求,这个时候需要去掉一条边,这样肯定有两个节点分别减少一个度,变成两个偶数度节点。于是问题变成了去掉那条边。

出题者给的答案是,我们只有16条边,整一个bitmap,遍历所有的边的组合。然后看哪个组合得到的和最大。时间复杂度为n * 2^16。嗯。。。。。其实挺好的。但是我觉得太复杂了。看了下答案,有复杂度为n的解。于是我研究了一下。奥,他们直接从一个节点开始dfs,遇到的节点加入进来,计算度数。如果有四个奇数度的节点在这个子图,那么需要去掉一条边,很多人直接拿一条权重最低的边一减就完事了,并且AC了。

感觉哪里不对劲的我把AC代码抄下来丢上去。跑test,一切都很愉悦,直到第117个test case,嗯???WA了?为嘛他能过,我一样的就WA,一看记录,比赛的时候只有116个test case。。。

仔细分析test case117,发现他的权重最小的那条边,是一个割边,去除之后,有一个节点从原来的欧拉图上面割裂开了。。。。看来codeforeces的测试用例也不是很全嘛。。。。

正确的做法是。将任意两个点(包括从自己出发到自己)的边的min值保存下来。先做一次dfs,这个dfs需要做的就是遍历四个点,将连通子图的权重和求出来,如果这个子图的奇数节点为4,那就丢掉这个结果。然后,遍历16条边,如果这个边在图里面出现了,不管是有一条还是几条,尝试将它抹去,然后做一次dfs,更新结果,再把这个边加回来。时间复杂度 4*4*n。很愉悦。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. int deg[4];
  4. vector<int> e[4][4];
  5. bool was[4];
  6. unsigned int min_v[4][4];
  7. int sum, min_val, cnt_node, cnt_deg, res;
  8. vector<int> removed;
  9. void dfs(int u) {
  10. was[u] = true;
  11. cnt_deg += deg[u];
  12. cnt_node += 1;
  13. for (int c : e[u][u]) {
  14. sum += c;
  15. }
  16. for (int v = 0; v < 4; ++v) {
  17. if (!e[u][v].empty()) {
  18. if (u < v) {
  19. for (int c : e[u][v]) {
  20. sum += c;
  21. }
  22. }
  23. if (!was[v]) dfs(v);
  24. }
  25. }
  26. }
  27. void check()
  28. {
  29. memset(was, 0, sizeof(was));
  30. for (int i = 0; i < 4; ++i) if (!was[i]) {
  31. sum = 0;
  32. cnt_deg = 0;
  33. dfs(i);
  34. if (cnt_deg != 4) {
  35. res = max(res, sum);
  36. }
  37. }
  38. // printf("res:%d\n", res);
  39. }
  40. //void print_all_edeges()
  41. //{
  42. // for (int i = 0; i < 4; ++i) {
  43. // for (int j = 0; j <= i; ++j) {
  44. // if(e[i][j].size() > 0)
  45. // {
  46. // printf("i:%d, j:%d, size:%d\n", i + 1, j +1, e[i][j].size());
  47. // }
  48. // }
  49. // }
  50. // printf("\n");
  51. //}
  52. int main() {
  53. int n; cin >> n;
  54. res = 0;
  55. memset(min_v, 0xff, sizeof(min_v));
  56. for (int i = 0; i < n; ++i) {
  57. int u, v, c;
  58. cin >> u >> c >> v;
  59. deg[--u] ^= 1;
  60. deg[--v] ^= 1;
  61. e[u][v].push_back(c);
  62. if(c < min_v[u][v])
  63. {
  64. min_v[u][v]=c;
  65. min_v[v][u]=c;
  66. }
  67. if (u != v) e[v][u].push_back(c);
  68. }
  69. check();
  70. for (int j = 0; j < 4; j++)
  71. {
  72. for(int k = 0; k <= j; k++)
  73. {
  74. //delete one edge
  75. if(e[j][k].size() > 0)
  76. {
  77. auto i = e[j][k].begin();
  78. // printf("min_v[j][k] is :%d %d\n", min_v[j][k], min_v[k][j]);
  79. for(;i != e[j][k].end(); i++)
  80. {
  81. if(*i == min_v[j][k])
  82. {
  83. e[j][k].erase(i);
  84. break;
  85. }
  86. }
  87. auto it = e[k][j].begin();
  88. if(j!=k)
  89. {
  90. for(;it != e[k][j].end(); it++)
  91. {
  92. if(*it == min_v[k][j])
  93. {
  94. e[k][j].erase(it);
  95. break;
  96. }
  97. }
  98. }
  99. // printf("after remove %d, %d\n", k + 1, j+ 1);
  100. // print_all_edeges();
  101. deg[j] ^= 1;
  102. deg[k] ^= 1;
  103. check();
  104. //add back after check
  105. e[j][k].push_back(min_v[j][k]);
  106. if(j!=k)e[k][j].push_back(min_v[j][k]);
  107. deg[j] ^= 1;
  108. deg[k] ^= 1;
  109. // printf("after recover\n");
  110. // print_all_edeges();
  111. }
  112. }
  113. }
  114. cout << res << endl;
  115. return 0;
  116. }

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号