在这里简单说一些我出的题目。
F: Pie
此题很明显的是一个最小匹配的模型。而后根据题目数据的特点可以利用DP来解决。
简单说一下性质:
1. 排序之后的匹配是不存在交叉线的。也就是说,如果第i高的男的和第j高的女的匹配,那么比i矮的男的就不能和比j高的女的匹配。同理,比i高的男的也就不能和比j矮的女的匹配。
2. 题目里有说男女的人数相差不到100,所以我们可以知道一个人的的可选匹配数量最多是100个。再加上这个性质,我们就可以用O(n * 100) = O(1000000)的DP来解决这个问题。相关的实现要用到滚存。
附上代码:
Pie.cpp
- /*
- * Author: momodi
- * Created Time: 2010-4-11 15:24:02
- * File Name: Pie.cpp
- */
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <cstdlib>
- #include <algorithm>
- #include <vector>
- using namespace std;
- #define out(v) cerr << #v << “: “ << (v) << endl
- #define SZ(v) ((int)(v).size())
- const int maxint = -1u>>1;
- template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
- template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
- const int maxn = 10000;
- double dtn[maxn], dtm[maxn];
- int n, m;
- double f[2][maxn + 1];
- double solve() {
- memset(f, 0, sizeof(f));
- f[0][0] = f[1][0] = 0;
- for (int i = 1; i <= n; ++i) {
- double *now = f[1 ^ (i & 1)], *pre = f[i & 1];
- now[i - 1] = 1e100;
- for (int j = i; j + (n – i) <= m; ++j) {
- now[j] = min(now[j - 1], pre[j - 1] + abs(dtn[i - 1] – dtm[j - 1]));
- }
- }
- return f[1 ^ (n & 1)][m];
- }
- int main() {
- //freopen(“Pie.out”, “w”, stdout);
- while (scanf(“%d %d”, &n, &m) == 2 && (n || m)) {
- for (int i = 0; i < n; ++i) {
- scanf(“%lf”, dtn + i);
- }
- for (int i = 0; i < m; ++i) {
- scanf(“%lf”, dtm + i);
- }
- sort(dtn, dtn + n);
- sort(dtm, dtm + m);
- if (n > m) {
- for (int i = 0; i < n; ++i) {
- swap(dtn[i], dtm[i]);
- }
- swap(n, m);
- }
- printf(“%.6f\n”, solve());
- }
- return 0;
- }
G: Precious
这个题说白了就是判断一个简单多边形内部的两个点是不是可见的。做法也很简单,可能大家都对几何不感冒,不是很愿意去做。
判断两个点是不是可见的做法:
1. 用两个点来做一个端点都是开的线段,然后和多边形求交点,如果有交点的话,那么就是不可见的。
2. 取这个线段的中点来看看这个点是不是在简单多边形内部,如果不在内部的话,那么也是不可见的。
满足上面两个条件的就都是可见的。
知道了任意两个点是不是可见的之后,建立一个图,然后跑一下最短路就好了。
代码:
Precious.cpp
- /*
- * Author: momodi
- * Created Time: 2010-4-14 15:55:08
- * File Name: Precious.cpp
- */
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <cstdlib>
- #include <algorithm>
- #include <vector>
- using namespace std;
- #define out(v) cerr << #v << “: “ << (v) << endl
- #define SZ(v) ((int)(v).size())
- const int maxint = -1u>>1;
- template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
- template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
- const double eps = 1e-9;
- int sgn(double a) {
- return (a > eps) – (a < -eps);
- }
- #define SQR(v) ((v) * (v))
- struct P {
- double x, y;
- P(double x, double y):x(x), y(y){}
- P():x(0), y(0){}
- double cross(P a, P b) const {
- return (a.x – x) * (b.y – y) – (a.y – y) * (b.x – x);
- }
- double dot(P a, P b) const {
- return (a.x – x) * (b.x – x) + (a.y – y) * (b.y – y);
- }
- double dist(P a) const {
- return sqrt(SQR(a.x – x) + SQR(a.y – y));
- }
- };
- const int maxn = 110;
- int n, m;
- P poly[maxn];
- int type[maxn];
- bool inter(P a, P b, P c, P d) {
- double s1 = a.cross(b, c);
- double s2 = a.cross(b, d);
- if (sgn(s1 – s2) == 0) {
- return false;
- }
- P t = P((c.x * s2 – d.x * s1) / (s2 – s1), (c.y * s2 – d.y * s1) / (s2 – s1));
- return sgn(t.dot(a, b)) < 0 && sgn(t.dot(c, d)) <= 0;
- }
- int inside(const P &p) {
- int ans = -1;
- for (P *it = poly + n – 1, *jt = poly; jt != poly + n; it = jt++) {
- double t = it->y < jt->y ? it->cross(*jt, p) : jt->cross(*it, p);
- if (sgn(t) == 0 && sgn(p.dot(*it, *jt)) <= 0) {
- return 0; // point p is on the polygon.
- }
- if (sgn(t) < 0 && ((sgn(it->y – p.y) < 0) ^ (sgn(jt->y – p.y) < 0))) {
- ans *= -1;
- }
- }
- return ans; //if ans == 1 point is in the polygon and if ans == -1 point is out the polygon.
- }
- bool conn(P S, P T) {
- if (inside(P((S.x + T.x) / 2, (S.y + T.y) / 2)) == -1) {
- return false;
- }
- for (int i = n – 1, j = 0; j < n; i = j++) {
- if (inter(S, T, poly[i], poly[j])) {
- return false;
- }
- }
- return true;
- }
- double adj[maxn][maxn];
- double solve() {
- for (int i = 0; i <= n; ++i) {
- for (int j = i + 1; j <= n; ++j) {
- if (conn(poly[i], poly[j])) {
- adj[i][j] = adj[j][i] = poly[i].dist(poly[j]);
- } else {
- adj[i][j] = adj[j][i] = 1e100;
- }
- }
- }
- for (int k = 0; k <= n; ++k) {
- for (int i = 0; i <= n; ++i) {
- for (int j = 0; j <= n; ++j) {
- get_min(adj[i][j], adj[i][k] + adj[k][j]);
- }
- }
- }
- double tot = 0, OK = 0;
- for (int i = 0; i < n; ++i) {
- if (type[i] != 0) {
- ++tot;
- }
- }
- for (int i = 0; i < n; ++i) {
- for (int j = 0; j < n; ++j) {
- if (type[i] == -1 && type[j] == 1 && sgn(adj[n][i] + adj[n][j] – m) <= 0) {
- ++OK;
- }
- }
- }
- //for (int i = 0; i < n; ++i) {
- //printf(“%.2f “, adj[i][n]);
- //}
- //puts(“”);
- //printf(“%.0f %.0f %.0f\n”, I, O, tot);
- if (tot == 0) return 0;
- return OK / (tot * tot);
- }
- int main() {
- //freopen(“test.in”, “r”, stdin);
- freopen(“Precious.out”, “w”, stdout);
- while (scanf(“%d %d”, &n, &m) == 2 && (n || m)) {
- for (int i = 0; i <= n; ++i) {
- scanf(“%lf %lf”, &poly[i].x, &poly[i].y);
- if (i < n) {
- scanf(“%d”, type + i);
- }
- }
- printf(“%.9f\n”, solve());
- }
- return 0;
- }
H: Railway
题意就是边是属于无向图的0个环,1一个环,还是多个环。
这是一个利用双连通分量来求解的图论题。
把原来的图的双连通分量都求出来之后,判断每一个连通分量里面的点和边的数量的关系。
1. 点>边 那么这些边都是没有用的。
2. 点=边 那么这些边都是有用的。
3. 点<边 那么这些边都是会发生冲突的。
代码:
Railway.cpp
- /*
- * Author: momodi
- * Created Time: 2010-4-15 14:32:25
- * File Name: Railway.cpp
- */
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <cstdlib>
- #include <algorithm>
- #include <vector>
- #include <cassert>
- using namespace std;
- #define out(v) cerr << #v << “: “ << (v) << endl
- #define SZ(v) ((int)(v).size())
- const int maxint = -1u>>1;
- template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
- template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
- const int maxn = 10000;
- const int maxm = 100000;
- int n, m;
- vector<int> adj[maxn];
- int pre[maxn], low[maxn];
- int ans[3];
- int tim;
- void dfs(int f, int v, int &anstype, int &anscnt) {
- anstype = anscnt = 0;
- int newtype = 0, newcnt = 0;
- low[v] = pre[v] = tim++;
- for (vector<int>::const_iterator it = adj[v].begin(); it != adj[v].end(); ++it) {
- if (*it != f) {
- if (pre[*it] == -1) {
- dfs(v, *it, newtype, newcnt);
- ++newcnt;
- if (low[*it] >= pre[v]) {
- ans[min(2, newtype)] += newcnt;
- } else {
- get_min(low[v], low[*it]);
- anstype += newtype;
- anscnt += newcnt;
- }
- } else {
- get_min(low[v], pre[*it]);
- if (pre[*it] < pre[v]) {
- ++anstype;
- ++anscnt;
- }
- }
- }
- }
- }
- int main() {
- //freopen(“test.in”, “r”, stdin);
- //freopen(“Railway.out”, “w”, stdout);
- while (scanf(“%d %d”, &n, &m) == 2 && (n || m)) {
- for (int i = 0; i < n; ++i) {
- adj[i].clear();
- }
- for (int i = 0; i < m; ++i) {
- int u, v;
- scanf(“%d %d”, &u, &v);
- adj[u].push_back(v);
- adj[v].push_back(u);
- }
- tim = 0;
- fill(pre, pre + n, -1);
- fill(ans, ans + 3, 0);
- int type, cnt;
- for (int i = 0; i < n; ++i) {
- if (pre[i] == -1) {
- dfs(i, i, type, cnt);
- }
- }
- //fprintf(stderr, “%d %d %d\n”, ans[0], ans[1], ans[2]);
- assert(ans[0] + ans[1] + ans[2] == m);
- printf(“%d %d\n”, ans[0], ans[2]);
- }
- return 0;
- }
I: Special Fish
这题我觉得没啥意思。。凑数的。把一个点拆成两个点之后,建立一个模型,然后跑km就好了。
Code Snippet
- /*
- * Author: momodi
- * Created Time: 2010-4-11 15:08:20
- * File Name: SpecialFish.cpp
- */
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <cstdlib>
- #include <algorithm>
- #include <vector>
- using namespace std;
- #define out(v) cerr << #v << “: “ << (v) << endl
- #define SZ(v) ((int)(v).size())
- const int maxint = -1u>>1;
- template <class T> bool get_max(T& a, const T &b) {return b > a? a = b, 1: 0;}
- template <class T> bool get_min(T& a, const T &b) {return b < a? a = b, 1: 0;}
- struct Graph {
- static const int maxn = 100;
- int w[maxn][maxn], lx[maxn], ly[maxn], matx[maxn], maty[maxn], n;
- bool fx[maxn], fy[maxn];
- void clear() {
- memset(w, 0, sizeof(w));
- n = 0;
- }
- void insert(int u, int v, int c) {
- get_max(n, max(u + 1, v + 1));
- w[u][v] = c;
- }
- int match() {
- memset(ly, 0, sizeof(ly));
- for (int i = 0; i < n; ++i) {
- lx[i] = -maxint;
- for (int j = 0; j < n; ++j) {
- get_max(lx[i], w[i][j]);
- }
- }
- memset(matx, -1, sizeof(matx));
- memset(maty, -1, sizeof(maty));
- for (int i = 0; i < n; ++i) {
- memset(fx, false, sizeof(fx));
- memset(fy, false, sizeof(fy));
- if (!dfs(i)) {
- –i;
- int p = maxint;
- for (int k = 0; k < n; ++k) {
- if (fx[k] == true) {
- for (int j = 0; j < n; ++j) {
- if ((fy[j] == false)) {
- get_min(p, lx[k] + ly[j] – w[k][j]);
- }
- }
- }
- }
- for (int j = 0; j < n; ++j) {
- ly[j] += fy[j] * p;
- }
- for (int k = 0; k < n; ++k) {
- lx[k] -= fx[k] * p;
- }
- }
- }
- int ans = 0;
- for (int i = 0; i < n; ++i) {
- ans += w[maty[i]][i];
- }
- return ans;
- }
- bool dfs(int u) {
- fx[u] = 1;
- for (int v = 0; v < n; ++v) {
- if (lx[u] + ly[v] == w[u][v] && fy[v] == false) {
- fy[v] = true;
- if (maty[v] == -1 || dfs(maty[v])) {
- matx[u] = v;
- maty[v] = u;
- return true;
- }
- }
- }
- return false;
- }
- };
- Graph g;
- int main() {
- //freopen(“SpecialFish.out”, “w”, stdout);
- int n;
- while (scanf(“%d”, &n) == 1 && n) {
- vector<int> V(n);
- for (int i = 0; i < n; ++i) {
- scanf(“%d”, &V[i]);
- }
- vector<vector<bool> > adj(n, vector<bool>(n, 0));
- for (int i = 0; i < n; ++i) {
- char buf[n + 1];
- scanf(“%s”, buf);
- for (int j = 0; j < n; ++j) {
- if (buf[j] == ’1′) {
- adj[i][j] = true;
- }
- }
- }
- g.clear();
- for (int i = 0; i < n; ++i) {
- for (int j = 0; j < n; ++j) {
- if (adj[i][j]) {
- g.insert(i, j, V[i] ^ V[j]);
- }
- }
- }
- printf(“%d\n”, g.match());
- }
- return 0;
- }

我在公司比较忙,这次出题,其他题目我基本都没有看题意。整体上是xay把握的,所以其他题目的分析我就不说啦。标程和数据过会应该xay他们会发到官方网站上。
从Rank上来看,题目出的有点难了。。虽然我感觉题目和预赛的差不多。。G和H都ac的这么少,实在是出乎意料。
B题xay在赛前跟我说是难题,结果ac的这么多。。
A题xay说是sb题,结果没啥人ac。。
G题我以为会有5-6个队伍ac的。。结果也不对
反正基本没有猜对。。
Tags: WHU2010校赛决赛, 动态规划, 匹配, 图论, 计算几何
您好,请教一下H题,我给你一个图
5个点,6条边
1 2
1 3
2 3
3 4
4 5
3 5
这个图是应该算有用的呢?还是应该算冲突的呀?
其实关于这一点,题目描述中并没有说的很明白。
谢谢~
[Reply]
momodi Reply:
April 22nd, 2010 at 8:32 pm
这些点都属于一个环,都是好的。是怎么说清楚,图中环的定义不能有两个同样的点。
[Reply]
starvae Reply:
April 23rd, 2010 at 8:10 am
恩,我现在知道了,你所说的双联通分量是指点的双联通分量,不是边的双连通分量。
[Reply]