[itint5]直角路线遍历棋盘

http://www.itint5.com/oj/#22

这题一开始直接用暴力的DFS来做,果然到25的规模就挂了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
vector<bool> visited(50, false);
vector<vector<int> > vec_row(50);
vector<vector<int> > vec_col(50);
 
bool findPath(vector<int> &x, vector<int> &y, int idx, int depth, int direction) {
    if (depth == x.size()) return true;
    visited[idx] = true;
    if (direction == 0) {
        int row = x[idx];
        for (int i = 0; i < vec_row[row].size(); i++) {
            if (!visited[vec_row[row][i]]) {
                if (findPath(x, y, vec_row[row][i], depth+1, 1))
                    return true;
            }
        }
    } else {
        int col = y[idx];
        for (int i = 0; i < vec_col[col].size(); i++) {
            if (!visited[vec_col[col][i]]) {
                if (findPath(x, y, vec_col[col][i], depth+1, 0))
                    return true;
            }
        }
    }
     
    visited[idx] = false;
    return false;
}
 
 
//如果存在满足条件的遍历,返回true,否则返回false
bool existPath(vector<int> &x, vector<int> &y) {
    int k = x.size();
    if (k == 0) return true;
    for (int i = 0; i < k; i++) {
        vec_row[x[i]].push_back(i);
        vec_col[y[i]].push_back(i);
    }
    for (int i = 0; i < k; i++) {
        if (findPath(x, y, i, 1, 0) ||
            findPath(x, y, i, 1, 1)) {
                return true;
            }
    }
    return false;
}

正确的做法是转化成欧拉回路:http://www.itint5.com/discuss/22/%E7%9B%B4%E8%A7%92%E8%B7%AF%E7%BA%BF%E9%81%8D%E5%8E%86%E6%A3%8B%E7%9B%98

这题可以直接转换为欧拉回路(路径)问题,这样,如果有解的时候要输出遍历路径的时候,也比较好办了。
具体的转换方式为:n,m的棋盘,建一个包含n+m个顶点的图G(为了方便说明,类似二分图将其分为两列,左边n个顶点,右边m个顶点,分别代表n行和n列)。对于目标格子(i,j),左边第i个顶点和右边第j个顶点连一条边。最后的问题其实就是问转换之后的图G是否存在欧拉欧拉回路或者欧拉路径。
证明:相邻两步为直角,其实就是从某一行变到某一列。访问图G中的一条边,意味着访问棋盘中的一个目标点。由于图G中的边只连接左边的点(代表某一行)和右边的点(代表某一列),因此访问一条边就意味着从某一行变到了某一列,也就是转直角了。
所以问题变为能否从一点出发访问G中的所有边有且仅有一次。这个就是欧拉回路问题了。

 所以欧拉路径是:1.连通;2.奇点为2,为0时是欧拉回路。

 这里的连通我用并查集来做。注意写并查集的merge时,要先找到根,再merge。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
vector<int> djset;
 
int find(int i) {
    int x = i;
    while (djset[x] != x) {
        x = djset[x];
    }
    djset[i] = x;
    return x;
}
 
void merge(int i, int j) {
    djset[find(i)] = djset[find(j)];
}
 
//如果存在满足条件的遍历,返回true,否则返回false
bool existPath(vector<int> &x, vector<int> &y) {
    vector<int> axis(100, 0);
    // 计算奇点数目
    for (int i = 0; i < x.size(); i++) {
        axis[x[i]] = !axis[x[i]]; // 奇偶变换
    }
    for (int i = 0; i < y.size(); i++) {
        axis[y[i]+50] = !axis[y[i]+50];
    }
    int count = 0;
    for (int i = 0; i < axis.size(); i++) {
        if (axis[i]) count++;
    }
    if (count != 0 && count != 2) return false;
     
    djset.resize(x.size());
    for (int i = 0; i < djset.size(); i++) {
        djset[i] = i;
    }
     
    // 判断连通性
    for (int i = 0; i < x.size(); i++) {
        for (int j = i+1; j < x.size(); j++) {
            if (x[i] == x[j]) {
                merge(i, j);
            }
        }
    }
    for (int i = 0; i < y.size(); i++) {
        for (int j = i+1; j < y.size(); j++) {
            if (y[i] == y[j]) {
                merge(i, j);
            }
        }
    }
     
    for (int i = 0; i < x.size(); i++) {
        if (find(i) != find(0)) {
            return false;
        }
    }
    return true;
}

  

[itint5]直角路线遍历棋盘

上一篇:android:inputType参数类型说明


下一篇:[itint5]Excel数转换