HNOI2006(BZOJ1191~1197)题解

来源:互联网 发布:中国科学院软件研究所 编辑:程序博客网 时间:2024/05/18 02:45

06年的题目还是比较简单的……为防止查一道题时其他题被剧透而将题解部分调成白色了……



Day 1
超级英雄
将题目和锦囊分别视为二分图中两部分的点,一个题目向能用的锦囊连边,一边加边一边匹配当前题目,直到不能匹配的位置就是答案。由于每题只连出两条边,所以单次匹配可以优化到O(1)。不过裸的匈牙利也能过。


鬼谷子的钱袋
答案即log(n)取上整。


马步距离
这题其实有规律。如果对棋盘进行黑白染色,显然走奇数步只能走到与起点异色的点,偶数步只能走到同色点。假设走x步,那么终点到起点的曼哈顿距离应<=3x,且起点与终点的横坐标之差与纵坐标之差都应<=2x。那么一路枚举下去即可。注意特判走三步和走四步的一些特殊情况。


潘多拉的盒子
这题应该是最神的一道题,据说咒语机就是一个自动机……首先判断一个咒语机A是不是另一个B的升级有一种方法:
用二元组(x,y)表示当前状态处在A的x点与B的y点上,初始为(0,0)。转移时x和y一起走到加0和加1后的位置,即(px0,py0)和(px1,py1),如果已经访问过该状态就不转移。假设在某个状态中x为输出元而y不是,那么B就不能输出这条A能输出的咒语,即B不是A的升级。如果不存在这种状态,B就是A的升级。
用上面的方法对每两个咒语机都进行判断然后建图。建完之后缩强连通分量,得到的会是一个拓扑图。在这个拓扑图上DP即可。





Day 2
最短母串
最短的母串一定是将所有字串按一定顺序重叠排列。那么预处理出每两个串能重叠的最长长度,开始DP。记f[i][j]表示最短长度,其中i是二进制数,表示已经选择了哪些串,j是最后一个加入的串。转移时枚举每个串加入。答案即min{f[2^n-1][j]}。至于字典序最小,我的做法是保存每个f[i][j]对应的母串,转移时同时判断字典序。用字符数组几乎是卡着内存过的……用string会好一些,不过会慢一点……


公路修建问题
二分答案,用类似Kruskal的方法判断能够加入多少条一级公路,如果不少于k条再判断能加入的二级公路能否构成生成树。


花仙子的魔法
好像变成经典DP问题了……观察一维的情况:每加入一条线段最多增加两个区间。然后考虑二维的情况:已有两个相交的圆,现在加入第三个,可以发现如果把第三个圆展开成一条线段,其与其他两个圆相交的位置也对应到线段上,那么可以转化成一维两条线段的情况。由此得出DP:记f[i][j]为最多的区间数,其中i为维数,j为施法次数。方程:f[i][j]=f[i][j-1] + f[i-1][j-1]。


军机调度

有谁看懂了题求讲解……





代码:

超级英雄:

//BZOJ1191; Hero (HNOI2006); Bipartite Graph#include <cstdio>#include <cstdlib>#define N 1000#define M 1000struct edge{int next, node;}e[M << 1 | 1];int n, m, x, y, head[N + 1], tot = 0, match[N + 1], ans = 0;bool v[N + 1];inline void addedge(int a, int b){e[++tot].next = head[a];head[a] = tot, e[tot].node = b;}bool find(int x){for (int i = head[x]; i; i = e[i].next){int node = e[i].node;if (v[node]) continue;v[node] = true;if (!match[node] || find(match[node])){match[node] = x;return true;}}return false;}int main(){scanf("%d%d", &n, &m);for (int i = 1; i <= m; ++i){scanf("%d%d", &x, &y);++x, ++y;addedge(i, x), addedge(i, y);}for (int i = 1; !ans && i <= m; ++i){for (int j = 1; j <= n; ++j) v[j] = false;if (!find(i)) ans = i;}ans = !ans ? m : ans - 1;printf("%d\n", ans);return 0;}

鬼谷子的钱袋:

//BZOJ1192; 鬼谷子的钱袋 (HNOI2006);#include <cstdio>#include <cstdlib>typedef long long ll;ll n, x = 1LL;int ans = 0;int main(){scanf("%lld", &n);while (x < n) x <<= 1LL, ++ans;printf("%d\n", ans);}

马步距离:

//BZOJ1193; 马步距离; Observation#include <cstdio>#include <cstdlib>int x1, x2, y1_, y2, d, ans;int abs(int x){ return x < 0 ? -x : x; }int main(){scanf("%d%d%d%d", &x1, &y1_, &x2, &y2);d = abs(x1 - x2) + abs(y1_ - y2);for (ans = 0; ; ++ans){if (((x1 + y1_ & 1) ^ (x2 + y2 & 1)) != (ans & 1)) continue;if (d <= ans * 3 && abs(x1 - x2) <= ans * 2 && abs(y1_ - y2) <= ans * 2) break;}if (d == 1) ans = 3;if (d == 4 && abs(x1 - x2) == 2 && abs(y1_ - y2) == 2)ans = 4;printf("%d\n", ans);return 0;}

潘多拉的盒子:

//BZOJ1194; Pandora (HNOI2006);#include <cstdio>#include <cstdlib>#include <utility>#include <algorithm>#define S 50#define N 50#define INFI 12345678typedef std::pair<int, int> pair;#define pair(x, y) std::make_pair(x, y)int s, n[N + 1], m[N + 1], p[S + 1][N + 1][2], x, y, ans = 0;int cnt[N + 1], f[N + 1], ind[N + 1], v[N + 1][N + 1], ct = 0, Q[N + 1], h, t;bool con[N + 1][N + 1], out[N + 1][N + 1];pair cur, q[N * N + 1];inline bool check(int a, int b){++ct;h = t = 0;q[t++] = pair(0, 0);while (h < t){cur = q[h++];if (out[a][cur.first] && !out[b][cur.second]) return false;x = p[a][cur.first][0], y = p[b][cur.second][0];if (v[x][y] != ct) v[x][y] = ct, q[t++] = pair(x, y);x = p[a][cur.first][1], y = p[b][cur.second][1];if (v[x][y] != ct) v[x][y] = ct, q[t++] = pair(x, y);}return true;}int main(){scanf("%d", &s);for (int i = 1; i <= s; ++i){scanf("%d%d", n + i, m + i);for (int j = 1; j <= m[i]; ++j){scanf("%d", &x);out[i][x] = true;}for (int j = 0; j < n[i]; ++j)scanf("%d%d", &p[i][j][0], &p[i][j][1]);}for (int i = 1; i <= s; ++i)for (int j = 1; j <= s; ++j)if (i != j && check(i, j))con[i][j] = 1;for (int i = 1; i <= s; ++i) cnt[i] = 1;for (int i = 1; i <= s; ++i)for (int j = 1; j < i; ++j)if (cnt[j] && con[i][j] && con[j][i]){++cnt[j], cnt[i] = 0;break;}for (int i = 1; i <= s; ++i)for (int j = 1; j <= s; ++j)if (cnt[i] && cnt[j] && con[i][j]) ++ind[j];for (int i = 1; i <= s; ++i) f[i] = cnt[i];h = t = 0;for (int i = 1; i <= s; ++i)if (!ind[i] && cnt[i]) Q[t++] = i;while (h < t){int cur = Q[h++];for (int i = 1; i <= s; ++i)if (con[cur][i] && cnt[i] && ind[i]){--ind[i];f[i] = std::max(f[i], f[cur] + cnt[i]);if (!ind[i]) Q[t++] = i;}}for (int i = 1; i <= s; ++i)ans = std::max(ans, f[i]);printf("%d\n", ans);return 0;}

最短母串:

//BZOJ1195; 最短母串 (HNOI2006); State Compression DP#include <cstdio>#include <cstdlib>#include <cstring>#include <climits>#include <algorithm>#include <utility>#include <queue>#define N 12#define LEN 50typedef std::pair<int, int> pair;#define pair(x, y) std::make_pair(x, y)int n, l[N + 1], f[N + 1][1 << N], d[N + 1][N + 1], x, y, from[N + 1][1 << N];int ans = INT_MAX, tq[N + 1], top;char s[N + 1][LEN + 1], ts[N + 1][LEN * N + 1], str[N + 1][1 << N][LEN * N + 1], tmp[LEN * N + 1];bool inq[N + 1][1 << N];std::queue<pair> q;pair cur;int main(){scanf("%d", &n);for (int i = 1; i <= n; ++i) scanf("%s", s[i]);for (int i = 1; i < n; ++i)for (int j = i + 1; j <= n; ++j)if (strcmp(s[i], s[j]) > 0){strcpy(s[0], s[i]);memset(s[i], 0, sizeof(char) * l[i]);strcpy(s[i], s[j]);memset(s[j], 0, sizeof(char) * l[j]);strcpy(s[j], s[0]);}for (int i = 1; i <= n; ++i) l[i] = (int)strlen(s[i]);for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j){if (i == j) continue;d[i][j] = l[i];for (int k = std::max(0, l[i] - l[j]); k < l[i]; ++k)if (!strncmp(s[i] + k, s[j], std::min(l[i] - k, l[j]))){d[i][j] = k;break;}}for (int i = 1; i <= n; ++i)f[i][1 << i - 1] = 0, q.push(pair(i, 1 << i - 1)), inq[i][1 << i - 1] = true;while (!q.empty()){y = q.front().first, x = q.front().second;inq[y][x] = false;q.pop();for (int i = 1; i <= n; ++i)if (!(x & (1 << i - 1))){int nx = x | (1 << i - 1);if (!f[i][nx] || f[i][nx] > f[y][x] + d[y][i]){f[i][nx] = f[y][x] + d[y][i];from[i][nx] = y;strcpy(str[i][nx], str[y][x]);strncpy(str[i][nx] + f[y][x], s[y], d[y][i]);if (!inq[i][nx])q.push(pair(i, nx)), inq[i][nx] = true;}else if (f[i][nx] == f[y][x] + d[y][i]){strcpy(tmp, str[y][x]);strncpy(tmp + f[y][x], s[y], d[y][i]);tmp[f[y][x] + d[y][i] + 1] = 0;if (strcmp(tmp, str[i][nx]) < 0)strcpy(str[i][nx], tmp);}}}for (int i = 1; i <= n; ++i)if (f[i][(1 << n) - 1] + l[i] < ans)ans = f[i][(1 << n) - 1] + l[i], top = 1, tq[0] = i;else if (f[i][(1 << n) - 1] + l[i] == ans)tq[top++] = i;for (int i = 0; i < top; ++i){strcpy(ts[i], str[tq[i]][(1 << n) - 1]);strcpy(ts[i] + f[tq[i]][(1 << n) - 1], s[tq[i]]);}x = 0;for (int i = 1; i < top; ++i)if (strcmp(ts[x], ts[i]) > 0) x = i;printf("%s\n", ts[x]);return 0;}

公路修建问题:

//BZOJ1196; road (HNOI2006); Kruskal + Dichtomization#include <cstdio>#include <cstdlib>#define N 10000#define M 20000#define MAX 30000struct inedge{int a, b, w1, w2;}ie[M + 1];int n, m, f[N + 1], k;int find(int x){ return x == f[x] ? x : f[x] = find(f[x]); }inline bool check(int x){int cnt = 0;for (int i = 1; i <= n; ++i) f[i] = i;for (int i = 1; cnt < n && i <= m; ++i)if (ie[i].w1 <= x && find(ie[i].a) != find(ie[i].b))++cnt, f[find(ie[i].a)] = find(ie[i].b);if (cnt < k) return false;for (int i = 1; cnt < n && i <= m; ++i)if (ie[i].w2 <= x && find(ie[i].a) != find(ie[i].b))++cnt, f[find(ie[i].a)] = find(ie[i].b);if (cnt >= n - 1) return true;return false;}int main(){scanf("%d%d%d", &n, &k, &m);for (int i = 1; i <= m; ++i)scanf("%d%d%d%d", &ie[i].a, &ie[i].b, &ie[i].w1, &ie[i].w2);int l = 1, r = MAX;while (l < r){int mid = l + r >> 1;if (check(mid)) r = mid;else l = mid + 1;}printf("%d\n", l);return 0;}

花仙子的魔法:

//BZOJ1197; 花仙子的魔法 (HNOI2006); DP#include <cstdio>#include <cstdlib>#define N 15#define M 100typedef long long ll;int m, n;ll f[N + 1][M + 1];int main(){scanf("%d%d", &m, &n);for (int i = 1; i <= m; ++i) f[1][i] = 2 * i;for (int i = 1; i <= n; ++i) f[i][1] = 2;for (int i = 2; i <= n; ++i)for (int j = 2; j <= m; ++j)f[i][j] = f[i - 1][j - 1] + f[i][j - 1];printf("%lld\n", f[n][m]);return 0;}