您的位置:首页 > 其它

算法竞赛入门经典(第二版)-刘汝佳-第七章 暴力求解法 习题(2/18)

2016-03-16 13:23 495 查看

说明

本文是我对第七章18道习题的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。

另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第七章习题contest

如果想直接看某道题,请点开目录后点开相应的题目!!!

习题

习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。我目前从算法逻辑上仍然没有想明白。

如果有哪位大神知道,请不吝指点。

另外本题可以剪枝,可以在时间复杂度上有重大优化,请参考其他博客。

代码

[code]#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

const int N = 21;

int n, k;
vector<int> neigh
;
int v
;
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

;
        memset(G, 0, sizeof(G));
        n = k;
        while (scanf("%d%d", &a, &b), a || b) {
            n = max(n, max(a, b));
            G[a] = 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;
}


习7-2 UVA 225 黄金图形

[b]题意


平面上有k个障碍点。从(0,0)点出发,第一次走1个单位,第二次走2个单位,……,第n次走n个单位,恰好回到(0,0)。要求只能沿着东南西北方向走,且每次必须转弯90°(不能沿着同一个方向继续走,也不能后退)。走出的图形可以自交,但不能经过障碍点。

思路

首先这个题目的翻译是有问题的,漏掉了一个很重要的判断条件:每一个落脚点不能与前一个相同(出发时的原点不算)。

然后我就在不知情的情况下各种WA。后来参考了其它博客才通过的。

另外这个题我觉得条件约束给的不好,应该说清楚每个城市的坐标范围,给一个基本约束,比如说坐标范围在-100到100之间。我后来是参考其它博客定义的坐标范围。

不过,没有给坐标范围的话这个题也能做,用两个set分别存储故障点和落脚点,用于判重即可。我开始用了set,因为题意不清的原因提交后WA给改了,就成了现在的代码。

代码

[code]#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;
}


习7-3 UVA 211 多米诺效应

题意

思路

代码

[code]


习7-4 UVA 818 切断圆环链

题意

思路

代码

[code]


习7-5 UVA 690 流水线调度

题意

思路

代码

[code]


习7-6 UVA 12113 重叠的正方形

题意

思路

代码

[code]


习7-7 UVA 12558 埃及分数

题意

思路

代码

[code]


习7-8 UVA 12107 数字谜

题意

思路

代码

[code]


习7-9 UVA 1604 立体八数码问题

题意

思路

代码

[code]


习7-10 UVA 11214 守卫棋盘

题意

思路

代码

[code]


习7-11 UVA 12569 树上的机器人规划 (简单版)

题意

思路

代码

[code]


习7-12 UVA 1533 移动小球

题意

思路

代码

[code]


习7-13 UVA 817 数字表达式

题意

思路

代码

[code]


习7-14 UVA 307 小木棍

题意

思路

代码

[code]


习7-15 UVA 11882 最大的数

题意

思路

代码

[code]


习7-16 UVA 11846 找座位

题意

思路

代码

[code]


习7-17 UVA 11694 Gokigen Naname 谜题

题意

思路

代码

[code]


习7-18 UVA 10384 推门游戏

题意

思路

代码

[code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: