The Morning after Halloween UVA - 1601

链接:https://vjudge.net/problem/UVA-1601

这题折腾了好几天,就因为想用A*写,不过到现在我还没搞明白怎么回事。。

中间查了维基百科才知道,只有估价函数h满足三角不等式就可以避免将重复结点加入队列,但这个三角不等式指的是h(n) <= c(n,p) + h(p), c指的是实际的花费。不过在这题中我最终证明了自己的估计函数满足这个条件,但还是过不了。最终在网上找大佬的代码,发现中间的判重是每次取f较小的,也就是说会有重复的节点被遍历到。我最终尝试数学证明,只能证出终点不会被重复加入(这也是为什么判断队首等于目标就可以直接退出的原因),但其他的点证不出来。。最终尝试改了代码,发现确实输出了正确的数据。到这里人彻底蒙了。。

一交,还是WA。。哭了。终于最后突发灵感,发现memcmp比较了未定义的区域。又吃了一回教训。。

不过时间效率感人,实在不会优化,真的tcl。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <queue>
#include <vector>
//#define fre
//#define DEBUG
using namespace std;
const int M = 4000000, mx[4] = {0, 0, 1, -1}, my[4] = {1, -1, 0, 0};
struct Point
{
    int x, y;
    Point(int x = 0, int y = 0): x(x), y(y) {}
    bool operator == (const Point& b) const
    {
        return x == b.x && y == b.y;
    }
    int operator - (const Point& b) const
    {
        return abs(x - b.x) + abs(y - b.y);
    }
};
struct Node
{
    Point p[3];
    int g, f;
    bool operator < (const Node& b) const
    {
        return f > b.f;
    }
};
int base[6], n, graph[20][20];
Point tar[3];
int hashtable[M], pointhash[20][20], cnt; //pointhash按照点的个数编码,hashtable按照点集编码
vector<Point> avail[20][20];
void init()
{
    base[0] = 1;
    for (int i = 1; i < 6; ++i) base[i] = base[i - 1] * 148;
}
inline int getpointhash(Point& t)
{
    return pointhash[t.x][t.y];
}
inline int gethash(Node& t)
{
    int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans += getpointhash(t.p[i]) * base[i];
    }
    return ans;
}
inline int H(Node& t)
{
    int ans = 0;
    for (int i = 0; i < n; ++i)
    {
        ans = max(ans, t.p[i] - tar[i]);
    }
    return ans;
}
bool inserthash(Node& t)
{
    int h = gethash(t);
    t.f = t.g + H(t);
    if (hashtable[h] <= t.f) return false;
    else hashtable[h] = t.f;
    return true;
}
inline bool legal(Node& hd, Node& tl)
{
    if (n == 3 && (tl.p[0] == tl.p[1] || tl.p[1] == tl.p[2] || tl.p[0] == tl.p[2])) return false;
    else if (n == 2 && tl.p[0] == tl.p[1]) return false;
    for (int i = 0; i < n; ++i)
    {
        for (int j = i + 1; j < n; ++j)
        {
            if (tl.p[i] == hd.p[j] && tl.p[j] == hd.p[i]) return false;
        }
    }
    return true;
}
int solve(Node& start)
{
    bool f = true;
    priority_queue<Node> q;
    Node hd, tl;
    q.push(start);
    inserthash(start);
    int sze[3];
    while (!q.empty())
    {
        f = true;
        hd = q.top(), q.pop();
        #ifdef DEBUG
        for (int i = 0; i < n; ++i)
            cout << hd.p[i].x << "  " << hd.p[i].y << "   ";
        cout << hd.g <<  endl;
        #endif
        //不能用memcmp,除非已统一初始化
        for (int i = 0; i < n; ++i)
        {
            if (!(hd.p[i] == tar[i]))
            {
                f = false;
                break;
            }
        }
        if (f) return hd.g;
        tl.g = hd.g + 1;
        for (int i = 0; i < n; ++i) sze[i] = avail[hd.p[i].x][hd.p[i].y].size();
        int kase = sze[0] - 1;
        for (int i = 1; i < n; ++i)
        {
            kase = kase * sze[i] + sze[i] - 1;
        }//神奇的编码方式。。
        for (int i = 1; i <= kase; ++i)
        {
            int j = i;
            for (int k = n - 1; k >= 0; --k)
            {
                tl.p[k] = avail[hd.p[k].x][hd.p[k].y][j % sze[k]];
                j /= sze[k];
            }
            if (legal(hd, tl) && inserthash(tl))
            {
                q.push(tl);
            }
        }
    }
    return -1;
}
int main()
{
    #ifdef fre
    freopen("in.in", "r", stdin);
    freopen("out.txt", "w", stdout);
    #endif
    int w, h, ans;
    char c;
    init();
    while (scanf("%d%d%d", &w, &h, &n) && w)
    {
        memset(hashtable, 0x3f3f3f3f, sizeof(hashtable));
        memset(pointhash, 0, sizeof(pointhash));
        memset(graph, 0, sizeof(graph));
        for (int i = 0; i < 20; ++i)
            for (int j = 0; j < 20; ++j) 
                avail[i][j].clear();
        cnt = 0;
        Node start;
        start.g = 0;
        getchar();
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
            {
                if ((c = getchar()) == '#') graph[i][j] = 1;
                else
                {
                    pointhash[i][j] = cnt++;
                    if (isupper(c)) tar[c - 'A'] = Point(i, j);
                    else if (islower(c)) start.p[c - 'a'] = Point(i, j);
                }               
            }    
            getchar();
        }   
        for (int i = 0; i < h; ++i)
        {    
            for (int j = 0; j < w; ++j)
            {
                if (!graph[i][j])
                {
                    avail[i][j].push_back(Point(i, j));
                    int nx, ny;
                    for (int k = 0; k < 4; ++k)
                    {
                        nx = i + mx[k], ny = j + my[k];
                        if (nx >= 1 && nx < h && ny >= 1 && ny < w && !graph[nx][ny])
                            avail[i][j].push_back(Point(nx, ny));
                    }
                }
            }
        }
        #ifdef DEBUG
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
                cout << avail[i][j].size() << " ";
            cout << endl;
        }
        #endif    
        start.f = start.g + H(start);
        printf("%d\n", solve(start));
    }
}

猜你喜欢

转载自www.cnblogs.com/jionkitten/p/12358109.html
今日推荐