Uva 1061 The Morning after Halloween

基本思路是BFS:

  1. 题目中已经说了,每相连的2X2格子中必有一个‘#’,也就是,每个点周围最多也就三个方向可以走。因此,可以把所有空格都提出来,形成一个图,直接遍历每条边,而不是每次判断4个方向是否可以走

  2.关于结点判重,最初的想法是想用一个六维数组,后来参考了其它,发现其实可以用一个三维数字代替,每个点可以用数字代替,因为题目中整个图最多为16X16,所以数字最大为16*16 = 256,这样做的另一个好处是,移动字母也方便了,可以用数字代替点,比如从(0, 0)移动到(0,1)可以考做是从 0 移动到 1

  3.N的数量不一,移动时要不要判断是N是多少?这是我刚开始的想法,个人采用递归的方法,先放一个点,再放下一个点,观察这是不是最后一个要移动的点,相对而言,比对每个N值进行单独处理要简单点。

注意点:题目输入的W,H,N中W是指宽度,即列数,而H即为行数。

收获及感悟:刚开始看到这题的时候已经吓着了,首先就是如何保存三个点状态,真的差点用六维数组或map了。在这里也学了一招,用整数依次标记每个点。另外,关于题意,刚开始理解不透,还在想a,b,c三者的先后移动顺序,后来发现想多了,任意顺序移动,判断移动后的状态是否可行即可。这题是在看了别人代码后才A掉的,非常感谢发布题解的同学。

参考资料:

  1.http://blog.csdn.net/acm_hkbu/article/details/42420503

  2.《算法竞赛入门经典(第2版)》

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 16 + 5;
char plan[MAXN][MAXN];
int W, H, N;
vector<int> link[MAXN*MAXN];
bool vis[MAXN*MAXN][MAXN*MAXN][MAXN*MAXN];
int dir[4][2] = {0,1, 0,-1, 1,0, -1,0};

struct State{
    int ghostPos[5], step;
};

State finalState, firstState;

// more than one ghost occupy a same position
// p is the prev state, whether two ghots in s exchange with their position
bool isCollisionOrExChange(State& s, State& p) {
    for(int i=0; i<N; ++i) {
        for(int j=0; j<N; ++j) {
            if(i!=j && s.ghostPos[i] == s.ghostPos[j]) {
                return true;
            }
            if(i!=j && s.ghostPos[i] == p.ghostPos[j] && s.ghostPos[j] == p.ghostPos[i]) {
                return true;
            }
        }
    }
    return false;
}

void Read() {
    for(int i=0; i<H; ++i) {
        cin.get();
        for(int j=0; j<W; ++j) {
            plan[i][j] = cin.get();
        }
    }
}

int GetVisValue(int x, int y) {
    return x * W + y;
}

void SetVis(State& t, bool flag) {
    int i, a[3]; // the positon of each ghost
    for(i=0; i<N; ++i) {
        a[i] = t.ghostPos[i];
    }
    // the number of ghosts may be less than 3
    for(; i<3; ++i) {
        a[i] = 0;
    }
    vis[a[0]][a[1]][a[2]] = flag;
}

bool IsVis(State& t) {
    int i, a[3]; // the positon of each ghost
    for(i=0; i<N; ++i) {
        a[i] = t.ghostPos[i];
    }
    // the number of ghosts may be less than 3
    for(; i<3; ++i) {
        a[i] = 0;
    }
    return vis[a[0]][a[1]][a[2]];
}

bool IsFinal(State& t) {
    for(int i=0; i<N; ++i) {
        if(finalState.ghostPos[i] != t.ghostPos[i]) {
            return false;
        }
    }
    return true;
}

// the ghost numbered g move
void NextState(State& s, int g, queue<State>& q, State& former) {
    for(size_t i=0; i<link[former.ghostPos[g]].size(); ++ i) {
        s.ghostPos[g] = link[former.ghostPos[g]][i];
        if(g == N-1 && !IsVis(s) && !isCollisionOrExChange(s, former)) {
            SetVis(s, true);
            q.push(s);
        } else if (g < N-1) {
            NextState(s, g+1, q, former);
        }
    }
}

// output the point of all ghosts
void Test(State& t) {
    for(int i=0; i<N; ++i) {
        cout << t.ghostPos[i] / W << " " << t.ghostPos[i] % W << endl;
    }
    cout << t.step << endl;
    cout << endl;
}

int Bfs() {
    memset(vis, false, sizeof(vis));
    queue<State> q;
    firstState.step = 0;
    q.push(firstState);
    SetVis(firstState, true);
    while(!q.empty()) {
        State t = q.front();
        q.pop();
        // Test(t);
        if(IsFinal(t)) {
            return t.step;
        }
        State newS;
        newS.step = t.step + 1;
        NextState(newS, 0, q, t);
    }
    return -1;
}

// Make a Graph
void MakeLink() {
    for(int i=0; i<H; ++ i) {
        for(int j=0; j<W; ++ j) {
            if(plan[i][j] != ‘#‘) {
                // the position means unmoving
                link[GetVisValue(i, j)].clear();
                link[GetVisValue(i, j)].push_back(GetVisValue(i, j));
                for(int k=0; k<4; ++k) { // four directions
                    int x = i + dir[k][0];
                    int y = j + dir[k][1];
                    if(x>=0 && x<H && y>=0 && y<W && plan[x][y]!=‘#‘) {
                        link[GetVisValue(i, j)].push_back(GetVisValue(x, y));
                    }
                }
            }
            if (isupper(plan[i][j])) {
                finalState.ghostPos[plan[i][j]-‘A‘] = GetVisValue(i, j);
            } else if (islower(plan[i][j])) {
                firstState.ghostPos[plan[i][j]-‘a‘] = GetVisValue(i, j);
            }
        }
    }
}

void Work() {
    Read();
    MakeLink();
    cout << Bfs() << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin >> W >> H >> N) {
        if(!W && !H && !N) {
            break;
        }
        Work();
    }
    return 0;
}

  

时间: 2024-08-03 13:29:35

Uva 1061 The Morning after Halloween的相关文章

[2016-02-24][UVA][1601][The Morning after Halloween]

时间:2016-02-24 15:49:41 星期三 题目编号:UVA 1601 题目大意:给定一个迷宫图,至多3个小写字母和等数目大写字符,求小写字母移动到大写字母的最少步数 迷宫宽度范围是4~16,字母个数是1~3, 分析:求最少步数,BFS,知道终点状态,可以用双向bfs优化 方法:BFS //单向bfs #include<iostream> #include<cstdio> #include<queue> #include<cstring> #inc

uva 1601 The Morning after Halloween

题意: 一张图,有空格,有障碍,有字母 每秒钟每个字母可以移动到相邻非障碍格 问所有的小写字母移动到大写字母所需的最少时间 https://vjudge.net/problem/UVA-1601 预处理每个状态有哪些后继状态 #include<queue> #include<cstdio> #include<cstring> #include<iostream> using namespace std; int m,n,p,s[3],t[3]; int dx

UVA 1601 The Morning after Halloween(搜索,二维数组转为图)

学习: 转化成图 1 //#include<bits/stdc++.h> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6 typedef long long ll; 7 int n,m,k; 8 char maps[20][20]; 9 int vis[200][200][200]; 10 int num[20][20]; 11 int

uva 11237 - Halloween treats(抽屉原理)

题目链接:uva 11237 - Halloween treats 题目大意:有c个小孩要到邻居家去要糖果,有n户邻居,每户邻居只会提供固定数量的糖果,熊孩子们为了不发生冲突,决定将取来的糖果平均分配,问说取那几家邻居的糖果可以做到平均分配,注意n ≥ c. 解题思路:抽屉原理,求出序列的前缀和,有n个,将前缀和对c取模后,根据剩余系定理肯定是在0~c-1之间的,如果是0那么答案就不用说了,如果两端前缀和同余,则说明中间该段的和是c的倍数.又因为n ≥ c,对于取0的时候肯定是可以有解的,那么n

UVA 11237 - Halloween treats(鸽笼原理)

11237 - Halloween treats 题目链接 题意:有c个小伙伴,n个房子(c <= n),每个房子会给ai个糖果,要求选一些房子,使得得到的糖果能平均分给小伙伴,输出方案 思路:c <= n 这个条件很关键,如果有这个条件,那么就可以开一个sum[i]记录0 - i的前缀和%c的值,这样一来在长度n的数组中,必然会出现重复的两个值,用sum[i] - sum[j] == 0这个区间就必然是所求的答案 代码: #include <cstdio> #include &l

拓展欧几里得详解 及其题目 POJ 1061 2115 2142 UVA 10673 10090

最近做了一些拓展欧几里得的题目呢,嘛,从一开始的不会到现在有点感觉,总之把我的经验拿出来和大家分享一下吧. 普通的欧几里得是用于解决求两个数a,b的gcd的,但是我们知道,gcd是线性组合 { ax+by | x,y∈Z }里的最小正元素(什么?不知道怎么来的?好吧...算法导论里数论算法那一章有证明),假若我们能够把这个x和y找出来,那么可以用来解决很多问题. (以下的gcd和lcm均指(gcd(a,b)和lcm(a,b)) 首先,假设ax+by=gcd这一个方程有一个特解x*,y*.那么显然

UVa 1601 || POJ 3523 The Morning after Halloween (BFS || 双向BFS &amp;&amp; 降维 &amp;&amp; 状压)

题意 :w*h(w,h≤16)网格上有n(n≤3)个小写字母(代表鬼).要求把它们分别移动到对应的大写字母里.每步可以有多个鬼同时移动(均为往上下左右4个方向之一移动),但每步结束之后任何两个鬼不能占用同一个位置,也不能在一步之内交换位置.输入保证所有空格连通,所有障碍格也连通,且任何一个2*2子网格中至少有一个障碍格.输出最少的步数.输入保证有解. 分析 :可以将当前所有小鬼的位置作为一个状态,然后模拟小鬼移动BFS拓展出其他可行状态并且顺带记录步数,直到所有的小鬼都到达终点.首先状态如何表示

UVA 4857 Halloween Costumes 区间背包

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2858 题意:给你n天须要穿的衣服的样式,每次能够套着穿衣服,脱掉的衣服就不能再用了(能够再穿),问至少要带多少条衣服才干參加全部宴会 分组背包模板题: dp[i][j];若第j件穿,dp[i][j]=d[i][j-1]+1; 若不穿.则能够想到.i到j-1区间内必须

UVA 4857 Halloween Costumes 分组背包

题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2858 题意:给你n天需要穿的衣服的样式,每次可以套着穿衣服,脱掉的衣服就不能再用了(可以再穿),问至少要带多少条衣服才能参加所有宴会 分组背包模板题: dp[i][j];若第j件穿,dp[i][j]=d[i][j-1]+1; 若不穿,则可以想到,i到j-1区间内必须