暴力搜索

习题

习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;}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值