Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
zmx0142857
GitHub Repository: zmx0142857/mini-games
Path: blob/master/c/skyscraper.c
363 views
1
#include <stdio.h>
2
#include <stdlib.h>
3
4
#define MAXN 99
5
#define UP 0
6
#define DOWN 1
7
#define LEFT 2
8
#define RIGHT 3
9
10
int N = 9;
11
int board[MAXN*MAXN]; // i 行 j 列: board[i*N+j]
12
int clue[MAXN*4]; // 上方第 j 条线索: clue[UP*N+j]
13
int stack[MAXN*MAXN];
14
int max_depth = 0;
15
int count = 0;
16
17
/**
18
* @param pos 开始位置 pos = i*N+j
19
* @param step 1: 从左到右, -1: 从右到左, N: 从上到下, -N: 从下到上
20
* @returns 能看到的高楼数
21
*/
22
int see(int pos, int step) {
23
int max = 0, cnt = 0;
24
for (int j = 0; j < N; j++) {
25
if (board[pos] > max) {
26
max = board[pos];
27
++cnt;
28
}
29
pos += step;
30
}
31
return cnt;
32
}
33
34
// 打印 clue
35
int print_clue() {
36
for (int i = 0; i < 4; i++) {
37
for (int j = 0; j < N; j++) {
38
printf("%d ", clue[i*N+j]);
39
}
40
puts("");
41
}
42
}
43
44
// 打印 board
45
int print_board() {
46
for (int i = 0; i < N; i++) {
47
for (int j = 0; j < N; j++) {
48
printf("%d ", board[i*N+j]);
49
}
50
puts("");
51
}
52
}
53
54
// 数字 num 在位置 pos 能否放置
55
int fit(int pos, int num) {
56
int i = pos / N, j = pos % N;
57
// 每行、每列的数字不重复
58
for (int k = 0; k < N; ++k) {
59
if (board[i*N+k] == num || board[k*N+j] == num) return 0;
60
}
61
62
// sudoku rule
63
// i -= i % 3;
64
// j -= j % 3;
65
// for (int k = i; k < i + 3; ++k) {
66
// for (int l = j; l < j + 3; ++l) {
67
// if (board[k*N+l] == num) return 0;
68
// }
69
// }
70
71
// skyscraper rule
72
board[pos] = num;
73
i = pos / N;
74
j = pos % N;
75
if (i == N-1 || (i == N-2 && board[pos + N] != 0)) {
76
if (clue[DOWN*N+j]) {
77
int see_down = see((N-1)*N+j, -N);
78
if (see_down != clue[DOWN*N+j]) {
79
board[pos] = 0;
80
return 0;
81
}
82
}
83
if (clue[UP*N+j]) {
84
int see_up = see(0*N+j, N);
85
if (see_up != clue[UP*N+j]) {
86
board[pos] = 0;
87
return 0;
88
}
89
}
90
}
91
if (j == N-1 || (j == N-2 && board[pos+1] != 0)) {
92
if (clue[RIGHT*N+i]) {
93
int see_right = see(i*N+(N-1), -1);
94
if (see_right != clue[RIGHT*N+i]) {
95
board[pos] = 0;
96
return 0;
97
}
98
}
99
if (clue[LEFT*N+i]) {
100
int see_left = see(i*N+0, 1);
101
if (see_left != clue[LEFT*N+i]) {
102
board[pos] = 0;
103
return 0;
104
}
105
}
106
}
107
108
return 1;
109
}
110
111
int solve_recur(int depth) {
112
if (depth == max_depth) {
113
puts("");
114
print_board();
115
++count;
116
} else {
117
int pos = stack[depth];
118
for (int num = N; num >= 1; num--) {
119
if (fit(pos, num)) {
120
board[pos] = num;
121
solve_recur(depth + 1);
122
board[pos] = 0;
123
}
124
}
125
}
126
}
127
128
int solve() {
129
for (int pos = 0; pos < N*N; pos++) {
130
if (board[pos] == 0) {
131
stack[max_depth++] = pos;
132
}
133
}
134
printf("%d blanks to solve\n", max_depth);
135
solve_recur(0);
136
}
137
138
int main(int argc, char *argv[]) {
139
if (argc != 2) {
140
puts("usage: ./skyscraper input.txt");
141
return 1;
142
}
143
FILE *fp = fopen(argv[1], "r");
144
145
// 第一个数字是棋盘大小 N
146
fscanf(fp, "%d", &N);
147
148
// 后面 4 行依次是上下左右的线索, 每行 N 个数
149
// 上、下的线索从左往右读取
150
// 左、右的线索从上往下读取
151
for (int pos = 0; pos < 4 * N; pos++) {
152
fscanf(fp, "%d", &clue[pos]);
153
}
154
155
// 填充 N*N 的 board
156
for (int pos = 0; pos < N * N; pos++) {
157
fscanf(fp, "%d", &board[pos]);
158
}
159
160
print_clue();
161
puts("");
162
print_board();
163
164
solve();
165
printf("%d solution(s)\n", count);
166
return 0;
167
}
168
169