8月份的时候计划做完Section 1 ~ 2。但是暑假结束的时候Section 2.4还差3道题,就搁置了。这两天做了一下。
TEXT Shortest Paths
最短路径。介绍了Dijkstra和Floyd-Warshall算法。
PROB The Tamworth Two
Farmer John和牛以某种方式在田地里转圈圈……问:能否相遇?最快何时相遇?
模拟。为了判定是否走进死循环,我弄了个6维数组。ANALYSIS提供了另一种思路:走400*400=160000步。
/*
ID: chrt2001
PROG: ttwo
LANG: C++
*/
#include <cstdio>
#include <algorithm>
using namespace std;
const int di[] = {-1, 0, 1, 0}, dj[] = {0, 1, 0, -1}, INF = 1<<30;
char M[12][12];
int S[12][12][4][12][12][4];
inline void move(int& i, int& j, int& k)
{
char y = M[i + di[k]][j + dj[k]];
if (y == '*') {
k = (k+1) % 4;
} else {
i += di[k];
j += dj[k];
}
}
int solve(int fi, int fj, int ci, int cj)
{
int fk = 0, ck = 0, t = 0;
for (int *p = &S[fi][fj][fk][ci][cj][ck]; !*p; p = &S[fi][fj][fk][ci][cj][ck]) {
if (fi == ci && fj == cj)
return t;
*p = t++;
move(fi, fj, fk);
move(ci, cj, ck);
}
return 0;
}
int main()
{
freopen("ttwo.in", "r", stdin);
freopen("ttwo.out", "w", stdout);
for (int i = 0; i <= 11; ++i)
M[0][i] = M[11][i] = M[i][0] = M[i][11] = '*';
char s[11];
int fi, fj, ci, cj;
for (int i = 1; i <= 10; ++i) {
scanf("%s", s);
for (int j = 1; j <= 10; ++j) {
M[i][j] = s[j-1];
if (s[j-1] == 'F') {
fi = i;
fj = j;
} else if (s[j-1] == 'C') {
ci = i;
cj = j;
}
}
}
printf("%d\n", solve(fi, fj, ci, cj));
return 0;
}
PROB Overfencing
迷宫有两个出口。寻找迷宫里距离出口的最短路最长的点。
BFS。其实可以把出口都丢进队列一起跑。
/*
ID: chrt2001
PROG: maze1
LANG: C++
*/
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int MAXW = 38*2+1, MAXH = 100*2+1, di[] = {-1, 1, 0, 0}, dj[] = {0, 0, -1, 1}, INF = 1<<30;
char M[MAXH][MAXW+2];
bool vis[MAXH][MAXW];
int c = 0, d[2][MAXH][MAXW];
struct Node {
int i, j;
};
void bfs(int i, int j)
{
memset(vis, 0, sizeof(vis));
vis[i][j] = true;
d[c][i][j] = 1;
queue<Node> Q;
Q.push((Node){i, j});
Node u;
while (!Q.empty()) {
u = Q.front();
Q.pop();
for (int k = 0; k < 4; ++k) {
Node v = (Node){u.i+di[k]*2, u.j+dj[k]*2};
if (M[u.i+di[k]][u.j+dj[k]] == ' ' && !vis[v.i][v.j]) {
vis[v.i][v.j] = true;
d[c][v.i][v.j] = d[c][u.i][u.j] + 1;
Q.push(v);
}
}
}
++c;
}
inline void check(int i, int j, int i1, int j1)
{
if (M[i][j] == '.')
bfs(i1, j1);
}
inline void subs(int i, int j)
{
if (M[i][j] == ' ')
M[i][j] = '.';
}
int main()
{
freopen("maze1.in", "r", stdin);
freopen("maze1.out", "w", stdout);
int W, H;
scanf("%d %d ", &W, &H);
W = W*2+1;
H = H*2+1;
for (int i = 0; i < H; ++i)
fgets(M[i], sizeof(char)*(W+2), stdin);
for (int i = 1; i < H; i += 2) {
subs(i, 0);
subs(i, W-1);
}
for (int j = 1; j < W; j += 2) {
subs(0, j);
subs(H-1, j);
}
for (int i = 1; i < H; i += 2) {
check(i, 0, i, 1);
check(i, W-1, i, W-2);
}
for (int j = 1; j < W; j += 2) {
check(0, j, 1, j);
check(H-1, j, H-2, j);
}
int ans = 0;
for (int i = 1; i < H; i += 2)
for (int j = 1; j < W; j += 2) {
int dist = INF;
for (int k = 0; k < 2; ++k)
if (d[k][i][j])
dist = min(dist, d[k][i][j]);
if (dist != INF)
ans = max(ans, dist);
}
printf("%d\n", ans);
return 0;
}
PROB Cow Tours
给定一个无向非连通图,每个点有二维坐标,距离是欧几里德距离。要求在两个不连通的点之间加一条边,使得新连通块的直径最小。
用Floyd-Warshall预处理一些东西,枚举即可。新的直径要么通过新加的边,要么不通过。
我用并查集给连通块编号,ANALYSIS用的是DFS。其实都不需要,Floyd-Warshall已经计算了足够的信息供我们编号。
/*
ID: chrt2001
PROG: cowtour
LANG: C++
*/
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAX_N = 150;
const double inf = 1LL<<60;
int x[MAX_N], y[MAX_N], root[MAX_N];
double M[MAX_N][MAX_N], d[MAX_N], D[MAX_N];
char s[MAX_N+1];
inline double sq(double x)
{
return x*x;
}
inline double dist(int a, int b)
{
return sqrt(sq(x[a] - x[b]) + sq(y[a] - y[b]));
}
int get_root(int x)
{
return x == root[x] ? x : root[x] = get_root(root[x]);
}
int main()
{
freopen("cowtour.in", "r", stdin);
freopen("cowtour.out", "w", stdout);
int n;
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d %d", &x[i], &y[i]);
root[i] = i;
}
for (int i = 0; i < n; ++i) {
scanf("%s", s);
for (int j = 0; j < n; ++j) {
if (s[j] == '1') {
M[i][j] = dist(i, j);
int rx = get_root(i), ry = get_root(j);
if (rx != ry)
root[rx] = ry;
} else if (i != j)
M[i][j] = inf;
}
}
for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
M[i][j] = min(M[i][j], M[i][k] + M[k][j]);
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (M[i][j] != inf)
d[i] = max(d[i], M[i][j]);
for (int i = 0; i < n; ++i) {
int r = get_root(i);
D[r] = max(D[r], d[i]);
}
double ans = inf;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (M[i][j] == inf)
ans = min(ans, max(d[i] + d[j] + dist(i, j), max(D[get_root(i)], D[get_root(j)])));
printf("%.6f\n", ans);
return 0;
}
PROB Bessie Come Home
求无向图中某个点集里的点到终点的最短路的最小值和对应的点,保证唯一。可能有重边和自环。
由于只有52个点,Floyd-Warshall即可。
由于把'a'..'z'
当作'a'..'y'
,WA一次。
/*
ID: chrt2001
PROG: comehome
LANG: C++
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
const int inf = 0x3f3f3f3f, N = 52;
int d[N][N];
template<typename T>
void relax(T& x, T v)
{
x = min(x, v);
}
inline int idx(char c)
{
return isupper(c) ? c-'A' : c-'a'+26;
}
int main()
{
freopen("comehome.in", "r", stdin);
freopen("comehome.out", "w", stdout);
int p;
scanf("%d", &p);
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
if (i != j)
d[i][j] = inf;
for (int i = 0; i < p; ++i) {
char x, y;
int w;
scanf(" %c %c %d", &x, &y, &w);
x = idx(x);
y = idx(y);
relax(d[x][y], w);
relax(d[y][x], w);
}
for (int k = 0; k < N; ++k)
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
relax(d[i][j], d[i][k] + d[k][j]);
int num, ans = inf;
for (int i = 0; i < 25; ++i)
if (d[25][i] < ans) {
ans = d[25][i];
num = i;
}
printf("%c %d\n", num+'A', ans);
return 0;
}
PROB Fractions to Decimals
分数转十进制小数,循环节用括号括起来,0循环不视作循环,整数加后缀.0
。
模拟长除法即可。当时一下没想到。前几天学习十进制小数转二进制小数,学到乘2取整法,于是这里用了乘10取整法。在ANALYSIS的提醒下……这不就是长除法吗?
还有一种方法,可以直接计算循环节前面的部分。没有明白,但是隐约感到和 NOI 2016 Day 1 T3 有某种联系。
从这道题我们也可以得出一个结论:既约分数p/q
的小数形式循环节至多有(q-1)位。
注意格式。76个字符换一行。一开始我把换行符的位置搞错,还以为看起来不对齐是因为.
和(
占用了不同于数字的宽度。ANALYSIS里用了printf
格式控制符%.*s
,*
是输出的最大字符数。
/*
ID: chrt2001
LANG: C++
PROG: fracdec
*/
#include <cstdio>
#include <cstring>
using namespace std;
const int MAX_N = 1e5;
char buf[MAX_N+9];
int next[MAX_N], p; // 分子 -> 下一个分子
inline void in(const char* s, int d = -1)
{
if (d == -1)
p += sprintf(buf+p, s);
else
p += sprintf(buf+p, s, d);
}
inline void out()
{
for (int i = 0; i < p; ++i) {
if (i && i%76 == 0)
putchar('\n');
putchar(buf[i]);
}
putchar('\n');
}
int main()
{
freopen("fracdec.in", "r", stdin);
freopen("fracdec.out", "w", stdout);
memset(next, -1, sizeof(next));
int n, d;
scanf("%d %d", &n, &d);
int k = n/d, head;
n -= k*d;
in("%d.", k);
if (!n) {
in("0");
out();
return 0;
}
for (head = n; next[n] == -1; n = next[n] = n*10%d)
;
for (int i = head; i != n; i = next[i])
in("%d", i*10/d);
if (n) {
in("(");
int i = n;
do {
in("%d", i*10/d);
i = next[i];
} while (i != n);
in(")");
}
out();
return 0;
}