知识点:深搜,剪枝,连通性判断
一开始看这个题觉得还是比较简单的,想着是不是又是难度虚高了,但是一写还是比较吃力的,第三次花了3个小时才过,而且是利用样例,输出递归中间的值才发现我的代码哪里出问题了,
首先这个题的框架不难定,就是一个指数枚举的模型,每个位置只有两种方法,最多49个空,那么时间复杂度就是2的49次方,要想办法剪枝,我们要开一个数组来记录每个方格里面放的是什么,记录每个点的字符是什么,然后还要开三个数组来记录信息,一个是记录点和多少个边相邻,一个记录点周围有多少个方块,一个记录点周围有多少个方块已经填了斜线了,每个点最好也标记一下标号,这样处理起来比较方便
首先是输入之后,题目保证有解,那么我们先把所有能确定的,只有一种填法的方格填上斜线,如果这个方格挨着0或者4的字符,那么显然可以确定它的填法,因为题目里面保证有解,所以这种一开始就只有一种填法的方格不需要判断是不是会违背题目的规则,但是其实还可以更细致,四个角的数字如果是1,四个边的非角的位置的数字如果是2,那么一样可以确定与其相邻的方格的斜线,但是这个太麻烦了,并且我的程序一开始就过了并且跑的不慢也就没有再想办法优化了
然后是深搜,一进去就要来一个剪枝,一开始因为没有写这个,这个剪枝就是,只要有一个是数字的点(注意不是方格)它周围还没填斜线的方格的数目小于它还需要连的斜线的数目,那么就剪枝,这也就是一开始弄那么多的数组的原因,这个应该属于可行性剪枝,因为题目说了,数字点周围连的线的个数必须恰好等于数字点的值,不能比数字多,同样不能比数字少,一开始就是这个剪枝没写,浪费了好几个小时,这个剪枝类似与李煜东组合枚举的写法,将指数枚举加上多不行,少也不行的剪枝,就变成了组合枚举,
然后又是两个剪枝,第一个是优化搜索顺序,我们找到还没填数字的方格里面可填方案个数最小的方格,找个过程中间,如果有没有填数字的方格可填斜线个数是0,那么直接剪枝,再搜下去也无解,这个应该也是属于可行性剪枝,这个剪枝和上面的剪枝,保证了我们最后得到的图案是符合题目要求,如果没被剪掉,那么我们就从当前找到的,备选方案最少的方格开始填数字向下递归,这个是从数独学的,一开始我们先填分支少的方格,这样轮到原本分支多的方格的时候,因为前面填的数字的影响,这时它的分支可能就不像一开始那么多了,总之还是一句老话,这样做不会是结果变得更差,故按照此顺序搜索
最后说说细节,比如判断一个方格可不可以连左上到右下的线,先判断左上,如果不是数字点可以,或者是数字点,它已经连的线的数目小于它需要连的线的数目,这样左上满足,同理判断右下,这两个判断完之后还要判断左上和右下两个点是不是联通,一开始想用并查集,但是发现这里有撤销的操作,还不会,就用了建图然后BFS的方法,因为邻接表是往表头后面插入的,所以我们分支回溯回来的时候,还原现场方法也很简单,类比遍历邻接表就知道了
至此这个题终于完成了,写了160多行。。。感觉还是有一定难度的,感觉我一个一点dp数学都不会的能写出来也算是不容易了,跑的速度是10ms,属于比较快的了,同时感悟,做一题会一题才是最好的,很多时候一个题目你AC了但是其实你的理解并不够的,这也是不能看刷题数的一个原因,
总结,这个题其实就是两个可行性剪枝,一个搜索顺序剪枝,然后一个连通性的维护和判断,一开始漏写了一个可行性剪枝,所以浪费了很多时间,凭借感性的感觉,这种可行性剪枝有种压缩上下界的感觉,不能只保证了上界,漏了下界
#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int n, a[N][N], b[N][N], c[N][N], flag, d[N][N], e[N][N];
int tot, ver[200], head[100], nxt[200];
char s[N][N];
void add(int x, int y) {
ver[++tot] = y;
nxt[tot] = head[x]; head[x] = tot;
}
void del(int x, int y) {
head[x] = nxt[head[x]];
head[y] = nxt[head[y]];
tot -= 2;
}
bool judge(int x, int i, int j) {
return (x == -1 || (x != -1 && b[i][j] < x) ? true : false);
}
bool bfs(int s1, int s2) {
queue<int> q;
q.push(s1);
int vis[100] = {0};
vis[s1] = 1;
while (!q.empty()) {
int now = q.front(); q.pop();
if (now == s2) return true;
for (int i = head[now]; i; i = nxt[i]) {
if (!vis[ver[i]]) {
vis[ver[i]] = 1;
q.push(ver[i]);
}
}
}
return false;
}
void change(int x, int y, int val) {
e[x - 1][y - 1] += val;
e[x - 1][y] += val;
e[x][y - 1] += val;
e[x][y] += val;
}
void dfs(int dep) {
if (flag) return;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
if (!isdigit(s[i][j])) continue;
int tmp = s[i][j] - '0';
if (d[i][j] - e[i][j] < tmp - b[i][j]) return;
}
}
if (!dep) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%c", a[i][j] == 1 ? '\\' : '/');
}
printf("\n");
}
flag = 1;
return;
}
int Min = 3, x, y, rec1, rec2;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (a[i][j]) continue;
int lu = -1, ld = -1, ru = -1, rd = -1;
if (isdigit(s[i - 1][j - 1])) lu = s[i - 1][j - 1] - '0';
if (isdigit(s[i - 1][j])) ru = s[i - 1][j] - '0';
if (isdigit(s[i][j - 1])) ld = s[i][j - 1] - '0';
if (isdigit(s[i][j])) rd = s[i][j] - '0';
int t1 = 0, t2 = 0;
if (judge(lu, i - 1, j - 1) && judge(rd, i, j) && !bfs(c[i - 1][j - 1], c[i][j])) t1 = 1;
if (judge(ru, i - 1, j) && judge(ld, i, j - 1) && !bfs(c[i][j - 1], c[i - 1][j])) t2 = 1;
if (t1 + t2 == 0) return;
else if (t1 + t2 < Min) {
Min = t1 + t2;
x = i; y = j;
rec1 = t1; rec2 = t2;
}
}
}
if (rec1) {
a[x][y] = 1;
b[x - 1][y - 1]++; b[x][y]++;
change(x, y, 1);
add(c[x - 1][y - 1], c[x][y]);
add(c[x][y], c[x - 1][y - 1]);
dfs(dep - 1);
a[x][y] = 0;
b[x - 1][y - 1]--; b[x][y]--;
change(x, y, -1);
del(c[x - 1][y - 1], c[x][y]);
}
if (rec2) {
a[x][y] = 2;
b[x - 1][y]++; b[x][y - 1]++;
change(x, y, 1);
add(c[x - 1][y], c[x][y - 1]);
add(c[x][y - 1], c[x - 1][y]);
dfs(dep - 1);
a[x][y] = 0;
b[x - 1][y]--; b[x][y - 1]--;
change(x, y, -1);
del(c[x - 1][y], c[x][y - 1]);
}
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(d, 0, sizeof(d));
memset(e, 0, sizeof(e));
tot = 0;
memset(head, 0, sizeof(head));
scanf("%d", &n);
for (int i = 0; i <= n; i++) {
scanf("%s", s[i]);
}
int cnt = 0;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
c[i][j] = ++cnt;
if ((!i && !j) || (!i && j == n) || (i == n && !j) || (i == n && j == n)) d[i][j] = 1;
else if (!i || !j || i == n || j == n) d[i][j] = 2;
else d[i][j] = 4;
}
}
int res = n * n;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
char ch1 = s[i - 1][j - 1], ch2 = s[i - 1][j];
char ch3 = s[i][j - 1], ch4 = s[i][j];
if (ch1 == '4' || ch4 == '4' || ch2 == '0' || ch3 == '0') {
a[i][j] = 1; res--;
b[i - 1][j - 1]++; b[i][j]++;
add(c[i - 1][j - 1], c[i][j]);
add(c[i][j], c[i - 1][j - 1]);
change(i, j, 1);
}
if (ch2 == '4' || ch3 == '4' || ch1 == '0' || ch4 == '0') {
a[i][j] = 2; res--;
b[i - 1][j]++; b[i][j - 1]++;
add(c[i - 1][j], c[i][j - 1]);
add(c[i][j - 1], c[i - 1][j]);
change(i, j, 1);
}
}
}
flag = 0;
dfs(res);
}
return 0;
}