msgbartop
My technology blog
msgbarbottom

18 Apr 10 武汉大学第八届程序设计竞赛部分决赛题目分析

在这里简单说一些我出的题目。

F: Pie

此题很明显的是一个最小匹配的模型。而后根据题目数据的特点可以利用DP来解决。

简单说一下性质:

1. 排序之后的匹配是不存在交叉线的。也就是说,如果第i高的男的和第j高的女的匹配,那么比i矮的男的就不能和比j高的女的匹配。同理,比i高的男的也就不能和比j矮的女的匹配。

2. 题目里有说男女的人数相差不到100,所以我们可以知道一个人的的可选匹配数量最多是100个。再加上这个性质,我们就可以用O(n * 100) = O(1000000)的DP来解决这个问题。相关的实现要用到滚存。

附上代码:

Pie.cpp
  1. /*
  2. * Author: momodi
  3. * Created Time:  2010-4-11 15:24:02
  4. * File Name: Pie.cpp
  5. */
  6. #include <iostream>
  7. #include <cstdio>
  8. #include <cstring>
  9. #include <cmath>
  10. #include <cstdlib>
  11. #include <algorithm>
  12. #include <vector>
  13. using namespace std;
  14. #define out(v) cerr << #v << “: “ << (v) << endl
  15. #define SZ(v) ((int)(v).size())
  16. const int maxint = -1u>>1;
  17. template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
  18. template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
  19. const int maxn = 10000;
  20. double dtn[maxn], dtm[maxn];
  21. int n, m;
  22. double f[2][maxn + 1];
  23. double solve() {
  24. memset(f, 0, sizeof(f));
  25. f[0][0] = f[1][0] = 0;
  26. for (int i = 1; i <= n; ++i) {
  27. double *now = f[1 ^ (i & 1)], *pre = f[i & 1];
  28. now[i - 1] = 1e100;
  29. for (int j = i; j + (ni) <= m; ++j) {
  30. now[j] = min(now[j - 1], pre[j - 1] + abs(dtn[i - 1] – dtm[j - 1]));
  31. }
  32. }
  33. return f[1 ^ (n & 1)][m];
  34. }
  35. int main() {
  36. //freopen(“Pie.out”, “w”, stdout);
  37. while (scanf(“%d %d”, &n, &m) == 2 && (n || m)) {
  38. for (int i = 0; i < n; ++i) {
  39. scanf(“%lf”, dtn + i);
  40. }
  41. for (int i = 0; i < m; ++i) {
  42. scanf(“%lf”, dtm + i);
  43. }
  44. sort(dtn, dtn + n);
  45. sort(dtm, dtm + m);
  46. if (n > m) {
  47. for (int i = 0; i < n; ++i) {
  48. swap(dtn[i], dtm[i]);
  49. }
  50. swap(n, m);
  51. }
  52. printf(“%.6f\n”, solve());
  53. }
  54. return 0;
  55. }

G: Precious

这个题说白了就是判断一个简单多边形内部的两个点是不是可见的。做法也很简单,可能大家都对几何不感冒,不是很愿意去做。

判断两个点是不是可见的做法:

1. 用两个点来做一个端点都是开的线段,然后和多边形求交点,如果有交点的话,那么就是不可见的。

2. 取这个线段的中点来看看这个点是不是在简单多边形内部,如果不在内部的话,那么也是不可见的。

满足上面两个条件的就都是可见的。

知道了任意两个点是不是可见的之后,建立一个图,然后跑一下最短路就好了。

代码:

Precious.cpp
  1. /*
  2. * Author: momodi
  3. * Created Time:  2010-4-14 15:55:08
  4. * File Name: Precious.cpp
  5. */
  6. #include <iostream>
  7. #include <cstdio>
  8. #include <cstring>
  9. #include <cmath>
  10. #include <cstdlib>
  11. #include <algorithm>
  12. #include <vector>
  13. using namespace std;
  14. #define out(v) cerr << #v << “: “ << (v) << endl
  15. #define SZ(v) ((int)(v).size())
  16. const int maxint = -1u>>1;
  17. template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
  18. template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
  19. const double eps = 1e-9;
  20. int sgn(double a) {
  21. return (a > eps) – (a < -eps);
  22. }
  23. #define SQR(v) ((v) * (v))
  24. struct P {
  25. double x, y;
  26. P(double x, double y):x(x), y(y){}
  27. P():x(0), y(0){}
  28. double cross(P a, P b) const {
  29. return (a.xx) * (b.yy) – (a.yy) * (b.xx);
  30. }
  31. double dot(P a, P b) const {
  32. return (a.xx) * (b.xx) + (a.yy) * (b.yy);
  33. }
  34. double dist(P a) const {
  35. return sqrt(SQR(a.xx) + SQR(a.yy));
  36. }
  37. };
  38. const int maxn = 110;
  39. int n, m;
  40. P poly[maxn];
  41. int type[maxn];
  42. bool inter(P a, P b, P c, P d) {
  43. double s1 = a.cross(b, c);
  44. double s2 = a.cross(b, d);
  45. if (sgn(s1s2) == 0) {
  46. return false;
  47. }
  48. P t = P((c.x * s2d.x * s1) / (s2s1), (c.y * s2d.y * s1) / (s2s1));
  49. return sgn(t.dot(a, b)) < 0 && sgn(t.dot(c, d)) <= 0;
  50. }
  51. int inside(const P &p) {
  52. int ans = -1;
  53. for (P *it = poly + n – 1, *jt = poly; jt != poly + n; it = jt++) {
  54. double t = it->y < jt->y ? it->cross(*jt, p) : jt->cross(*it, p);
  55. if (sgn(t) == 0 && sgn(p.dot(*it, *jt)) <= 0) {
  56. return 0;   // point p is on the polygon.
  57. }
  58. if (sgn(t) < 0 && ((sgn(it->yp.y) < 0) ^ (sgn(jt->yp.y) < 0))) {
  59. ans *= -1;
  60. }
  61. }
  62. return ans; //if ans == 1 point is in the polygon and if ans == -1 point is out the polygon.
  63. }
  64. bool conn(P S, P T) {
  65. if (inside(P((S.x + T.x) / 2, (S.y + T.y) / 2)) == -1) {
  66. return false;
  67. }
  68. for (int i = n – 1, j = 0; j < n; i = j++) {
  69. if (inter(S, T, poly[i], poly[j])) {
  70. return false;
  71. }
  72. }
  73. return true;
  74. }
  75. double adj[maxn][maxn];
  76. double solve() {
  77. for (int i = 0; i <= n; ++i) {
  78. for (int j = i + 1; j <= n; ++j) {
  79. if (conn(poly[i], poly[j])) {
  80. adj[i][j] = adj[j][i] = poly[i].dist(poly[j]);
  81. } else {
  82. adj[i][j] = adj[j][i] = 1e100;
  83. }
  84. }
  85. }
  86. for (int k = 0; k <= n; ++k) {
  87. for (int i = 0; i <= n; ++i) {
  88. for (int j = 0; j <= n; ++j) {
  89. get_min(adj[i][j], adj[i][k] + adj[k][j]);
  90. }
  91. }
  92. }
  93. double tot = 0, OK = 0;
  94. for (int i = 0; i < n; ++i) {
  95. if (type[i] != 0) {
  96. ++tot;
  97. }
  98. }
  99. for (int i = 0; i < n; ++i) {
  100. for (int j = 0; j < n; ++j) {
  101. if (type[i] == -1 && type[j] == 1 && sgn(adj[n][i] + adj[n][j] – m) <= 0) {
  102. ++OK;
  103. }
  104. }
  105. }
  106. //for (int i = 0; i < n; ++i) {
  107. //printf(“%.2f “, adj[i][n]);
  108. //}
  109. //puts(“”);
  110. //printf(“%.0f %.0f %.0f\n”, I, O, tot);
  111. if (tot == 0) return 0;
  112. return OK / (tot * tot);
  113. }
  114. int main() {
  115. //freopen(“test.in”, “r”, stdin);
  116. freopen(“Precious.out”, “w”, stdout);
  117. while (scanf(“%d %d”, &n, &m) == 2 && (n || m)) {
  118. for (int i = 0; i <= n; ++i) {
  119. scanf(“%lf %lf”, &poly[i].x, &poly[i].y);
  120. if (i < n) {
  121. scanf(“%d”, type + i);
  122. }
  123. }
  124. printf(“%.9f\n”, solve());
  125. }
  126. return 0;
  127. }

H: Railway

题意就是边是属于无向图的0个环,1一个环,还是多个环。

这是一个利用双连通分量来求解的图论题。

把原来的图的双连通分量都求出来之后,判断每一个连通分量里面的点和边的数量的关系。

1. 点>边 那么这些边都是没有用的。

2. 点=边 那么这些边都是有用的。

3. 点<边 那么这些边都是会发生冲突的。

代码:

Railway.cpp
  1. /*
  2. * Author: momodi
  3. * Created Time:  2010-4-15 14:32:25
  4. * File Name: Railway.cpp
  5. */
  6. #include <iostream>
  7. #include <cstdio>
  8. #include <cstring>
  9. #include <cmath>
  10. #include <cstdlib>
  11. #include <algorithm>
  12. #include <vector>
  13. #include <cassert>
  14. using namespace std;
  15. #define out(v) cerr << #v << “: “ << (v) << endl
  16. #define SZ(v) ((int)(v).size())
  17. const int maxint = -1u>>1;
  18. template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
  19. template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
  20. const int maxn = 10000;
  21. const int maxm = 100000;
  22. int n, m;
  23. vector<int> adj[maxn];
  24. int pre[maxn], low[maxn];
  25. int ans[3];
  26. int tim;
  27. void dfs(int f, int v, int &anstype, int &anscnt) {
  28. anstype = anscnt = 0;
  29. int newtype = 0, newcnt = 0;
  30. low[v] = pre[v] = tim++;
  31. for (vector<int>::const_iterator it = adj[v].begin(); it != adj[v].end(); ++it) {
  32. if (*it != f) {
  33. if (pre[*it] == -1) {
  34. dfs(v, *it, newtype, newcnt);
  35. ++newcnt;
  36. if (low[*it] >= pre[v]) {
  37. ans[min(2, newtype)] += newcnt;
  38. } else {
  39. get_min(low[v], low[*it]);
  40. anstype += newtype;
  41. anscnt += newcnt;
  42. }
  43. } else {
  44. get_min(low[v], pre[*it]);
  45. if (pre[*it] < pre[v]) {
  46. ++anstype;
  47. ++anscnt;
  48. }
  49. }
  50. }
  51. }
  52. }
  53. int main() {
  54. //freopen(“test.in”, “r”, stdin);
  55. //freopen(“Railway.out”, “w”, stdout);
  56. while (scanf(“%d %d”, &n, &m) == 2 && (n || m)) {
  57. for (int i = 0; i < n; ++i) {
  58. adj[i].clear();
  59. }
  60. for (int i = 0; i < m; ++i) {
  61. int u, v;
  62. scanf(“%d %d”, &u, &v);
  63. adj[u].push_back(v);
  64. adj[v].push_back(u);
  65. }
  66. tim = 0;
  67. fill(pre, pre + n, -1);
  68. fill(ans, ans + 3, 0);
  69. int type, cnt;
  70. for (int i = 0; i < n; ++i) {
  71. if (pre[i] == -1) {
  72. dfs(i, i, type, cnt);
  73. }
  74. }
  75. //fprintf(stderr, “%d %d %d\n”, ans[0], ans[1], ans[2]);
  76. assert(ans[0] + ans[1] + ans[2] == m);
  77. printf(“%d %d\n”, ans[0], ans[2]);
  78. }
  79. return 0;
  80. }

I: Special Fish

这题我觉得没啥意思。。凑数的。把一个点拆成两个点之后,建立一个模型,然后跑km就好了。

Code Snippet
  1. /*
  2. * Author: momodi
  3. * Created Time:  2010-4-11 15:08:20
  4. * File Name: SpecialFish.cpp
  5. */
  6. #include <iostream>
  7. #include <cstdio>
  8. #include <cstring>
  9. #include <cmath>
  10. #include <cstdlib>
  11. #include <algorithm>
  12. #include <vector>
  13. using namespace std;
  14. #define out(v) cerr << #v << “: “ << (v) << endl
  15. #define SZ(v) ((int)(v).size())
  16. const int maxint = -1u>>1;
  17. template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
  18. template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
  19. struct Graph {
  20. static const int maxn = 100;
  21. int w[maxn][maxn], lx[maxn], ly[maxn], matx[maxn], maty[maxn], n;
  22. bool fx[maxn], fy[maxn];
  23. void clear() {
  24. memset(w, 0, sizeof(w));
  25. n = 0;
  26. }
  27. void insert(int u, int v, int c) {
  28. get_max(n, max(u + 1, v + 1));
  29. w[u][v] = c;
  30. }
  31. int match() {
  32. memset(ly, 0, sizeof(ly));
  33. for (int i = 0; i < n; ++i) {
  34. lx[i] = -maxint;
  35. for (int j = 0; j < n; ++j) {
  36. get_max(lx[i], w[i][j]);
  37. }
  38. }
  39. memset(matx, -1, sizeof(matx));
  40. memset(maty, -1, sizeof(maty));
  41. for (int i = 0; i < n; ++i) {
  42. memset(fx, false, sizeof(fx));
  43. memset(fy, false, sizeof(fy));
  44. if (!dfs(i)) {
  45. i;
  46. int p = maxint;
  47. for (int k = 0; k < n; ++k) {
  48. if (fx[k] == true) {
  49. for (int j = 0; j < n; ++j) {
  50. if ((fy[j] == false)) {
  51. get_min(p, lx[k] + ly[j] – w[k][j]);
  52. }
  53. }
  54. }
  55. }
  56. for (int j = 0; j < n; ++j) {
  57. ly[j] += fy[j] * p;
  58. }
  59. for (int k = 0; k < n; ++k) {
  60. lx[k] -= fx[k] * p;
  61. }
  62. }
  63. }
  64. int ans = 0;
  65. for (int i = 0; i < n; ++i) {
  66. ans += w[maty[i]][i];
  67. }
  68. return ans;
  69. }
  70. bool dfs(int u) {
  71. fx[u] = 1;
  72. for (int v = 0; v < n; ++v) {
  73. if (lx[u] + ly[v] == w[u][v] && fy[v] == false) {
  74. fy[v] = true;
  75. if (maty[v] == -1 || dfs(maty[v])) {
  76. matx[u] = v;
  77. maty[v] = u;
  78. return true;
  79. }
  80. }
  81. }
  82. return false;
  83. }
  84. };
  85. Graph g;
  86. int main() {
  87. //freopen(“SpecialFish.out”, “w”, stdout);
  88. int n;
  89. while (scanf(“%d”, &n) == 1 && n) {
  90. vector<int> V(n);
  91. for (int i = 0; i < n; ++i) {
  92. scanf(“%d”, &V[i]);
  93. }
  94. vector<vector<bool> > adj(n, vector<bool>(n, 0));
  95. for (int i = 0; i < n; ++i) {
  96. char buf[n + 1];
  97. scanf(“%s”, buf);
  98. for (int j = 0; j < n; ++j) {
  99. if (buf[j] == ’1′) {
  100. adj[i][j] = true;
  101. }
  102. }
  103. }
  104. g.clear();
  105. for (int i = 0; i < n; ++i) {
  106. for (int j = 0; j < n; ++j) {
  107. if (adj[i][j]) {
  108. g.insert(i, j, V[i] ^ V[j]);
  109. }
  110. }
  111. }
  112. printf(“%d\n”, g.match());
  113. }
  114. return 0;
  115. }

SPX`3W4KSFGGEBS(VFXN3WF

我在公司比较忙,这次出题,其他题目我基本都没有看题意。整体上是xay把握的,所以其他题目的分析我就不说啦。标程和数据过会应该xay他们会发到官方网站上。

从Rank上来看,题目出的有点难了。。虽然我感觉题目和预赛的差不多。。G和H都ac的这么少,实在是出乎意料。

B题xay在赛前跟我说是难题,结果ac的这么多。。

A题xay说是sb题,结果没啥人ac。。

G题我以为会有5-6个队伍ac的。。结果也不对

反正基本没有猜对。。

Tags: , , , ,

Reader's Comments

  1. |

    您好,请教一下H题,我给你一个图
    5个点,6条边
    1 2
    1 3
    2 3
    3 4
    4 5
    3 5

    这个图是应该算有用的呢?还是应该算冲突的呀?
    其实关于这一点,题目描述中并没有说的很明白。
    谢谢~

    [Reply]

    momodi Reply:

    这些点都属于一个环,都是好的。是怎么说清楚,图中环的定义不能有两个同样的点。

    [Reply]

    starvae Reply:

    恩,我现在知道了,你所说的双联通分量是指点的双联通分量,不是边的双连通分量。

    [Reply]

Leave a Comment