习题
习7-1 UVA 208 消防车
题意
输入一个n(n≤20)个结点的无向图以及某个节点k,按照字典序从小到大顺序输出从节点1到节点k的所有路径,要求结点不能重复经过。
思路
这个题要事先判断节点1是否可以到达节点k,否则会超时。有很多种方法可以判断:比如DFS遍历,或者用并查集等。
然后DFS遍历即可,但考虑到算法效率,可以采取回溯+剪枝的方案(当然不剪枝也是可以AC的,时间长一点而已)。
我这个题卡在判断节点1是否可以到达节点k这一步上很久。我的代码主函数中第6行原来是:
n = 0;
后来查了很久,才发现改成
n = k;
就能AC。
按照我原来的算法逻辑,n=0的情况下,所给数据有可能出现这一种情况:
如果给出的所有路径中出现的节点都小于k,这样得到的n将小于k。
而这时候节点1肯定无法到达节点k,第一步的判断应该可以给出正确答案。
但结果就是我不改的话会WA,改了就AC。我目前从算法逻辑上仍然没有想明白。
如果有哪位大神知道,请不吝指点。
另外本题可以剪枝,可以在时间复杂度上有重大优化,请参考其他博客。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 21;
int n, k;
vector<int> neigh[N];
int v[N];
int path_count;
vector<int> path;
bool dfs(int u)
{
if (u == k) return true;
for (int i = 0; i < neigh[u].size(); i++) {
if (!v[neigh[u][i]]) {
int x = neigh[u][i];
v[x] = 1;
if (dfs(x)) return true;
}
}
return false;
}
void find_path()
{
path_count++;
for (int i = 0; i < path.size(); i++)
printf("%d%c", path[i], i == path.size()-1 ? '\n' : ' ');
}
void search(int u)
{
if (u == k) { find_path(); return; }
for (int i = 0; i < neigh[u].size(); i++) {
if (!v[neigh[u][i]]) {
int x = neigh[u][i];
v[x] = 1;
path.push_back(x);
search(x);
path.resize(path.size()-1);
v[x] = 0;
}
}
}
int main()
{
int kase = 0;
while (scanf("%d", &k) != EOF) {
int a, b;
int G[N][N];
memset(G, 0, sizeof(G));
n = k;
while (scanf("%d%d", &a, &b), a || b) {
n = max(n, max(a, b));
G[a][b] = G[b][a] = 1;
}
for (int i = 1; i <= n; i++) {
neigh[i].clear();
for (int j = 1; j <= n; j++) {
if (G[i][j]) neigh[i].push_back(j);
}
}
printf("CASE %d:\n", ++kase);
memset(v, 0, sizeof(v));
v[1] = 1;
path_count = 0;
if (dfs(1)) {
path.clear();
memset(v, 0, sizeof(v));
v[1] = 1;
path.push_back(1);
search(1);
}
printf("There are %d routes from the firestation to streetcorner %d.\n", path_count, k);
}
return 0;
}
- 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
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
习7-2 UVA 225 黄金图形
题意
平面上有k个障碍点。从(0,0)点出发,第一次走1个单位,第二次走2个单位,……,第n次走n个单位,恰好回到(0,0)。要求只能沿着东南西北方向走,且每次必须转弯90°(不能沿着同一个方向继续走,也不能后退)。走出的图形可以自交,但不能经过障碍点。
思路
首先这个题目的翻译是有问题的,漏掉了一个很重要的判断条件:每一个落脚点不能与前一个相同(出发时的原点不算)。
然后我就在不知情的情况下各种WA。后来参考了其它博客才通过的。
另外这个题我觉得条件约束给的不好,应该说清楚每个城市的坐标范围,给一个基本约束,比如说坐标范围在-100到100之间。我后来是参考其它博客定义的坐标范围。
不过,没有给坐标范围的话这个题也能做,用两个set分别存储故障点和落脚点,用于判重即可。我开始用了set,因为题意不清的原因提交后WA给改了,就成了现在的代码。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
const char tow[] = "ensw";
const int dir[4][2] = {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
typedef pair<int, int> P;
int n, k;
int G[250][250];
const int OFF = 105;
set<P> block;
int path_count;
vector<int> path;
void found_path()
{
for (int i = 0; i < path.size(); i++)
printf("%c", tow[path[i]]);
printf("\n");
path_count++;
}
void dfs(P p)
{
if (path.size() == n) {
if (p == P(0, 0))
found_path();
return;
}
int m = path.size();
for (int i = 0; i < 4; i++) {
if (m && (path[m-1]+1)%4/2 == (i+1)%4/2) continue;
P p1 = p;
bool flag = true;
for (int j = 1; j <= m+1; j++) {
p1.first += dir[i][0];
p1.second += dir[i][1];
int x = p1.first, y = p1.second;
if (abs(x) > OFF || abs(y) > OFF || G[p1.first+OFF][p1.second+OFF] == -1) {flag = false; break;}
}
if (flag && G[p1.first+OFF][p1.second+OFF] != 1) {
path.push_back(i);
G[p1.first+OFF][p1.second+OFF] = 1;
dfs(p1);
G[p1.first+OFF][p1.second+OFF] = 0;
path.resize(m);
}
}
}
int main()
{
int kase;
scanf("%d", &kase);
for (int t = 1; t <= kase; t++) {
scanf("%d%d", &n, &k);
int x, y;
memset(G, 0, sizeof(G));
for (int i = 0; i < k; i++) {
scanf("%d%d", &x, &y);
G[x+OFF][y+OFF] = -1;
}
path_count = 0;
path.clear();
dfs(P(0, 0));
printf("Found %d golygon(s).\n\n", path_count);
}
return 0;
}