• POJ1054 The Troublesome Frog 搜索+剪枝 Or DP+Hash


    搜索+剪枝

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #define MAXN 5005
    using namespace std;
    
    /*
    题意:
        给定一个行数和列数固定的网格,再给定若干个点,这些点是使用矩形上网格线交叉点的
        坐标来唯一确定的R行C列的网格,左上角的坐标为(1,1)右下角的坐标为(R,C),问给定的
        这些点能够组成最长可能路径是多长. 路径的定义的如下的:由若干连续且间隔相等的
        点组成,方向可任意,起始点和结束点均在网格之外的任意空间.也就是只要满足网格内
        连续两点之间的间距相等就可以了. 路径上要求至少三个点,而且起点和终点一定要在
        网格的外部. 
        
    解法:
        搜索+剪枝
        直接枚举按照x,y排序的序列,直接枚举2个点作为开始点,有三个的地方需要剪枝,这样
        跑起来更快速 
    */
    
    int R, C, N;
    char G[MAXN][MAXN]; // 开char能保留更多信息,比bool划算 
    
    struct Node {
        int x, y;
    }e[5005];
    
    bool cmp(Node a, Node b) {
        if (a.x != b.x) {
            return a.x < b.x;
        } else {
            return a.y < b.y;
        }
    }
    
    bool judge(int x, int y) {
        if (x > 0 && x <= R && y > 0 && y <= C) {
            return true;    
        }
        return false;
    }
    
    int deal() {
        int ret = 2, length, cx, cy, dx, dy, px, py;
        for (int i = 1; i < N; ++i) { // N不会和任何点组成一个点对
            for (int j = i+1; j <= N; ++j) {
                length = 2, cx = e[j].x, cy = e[j].y, dx = e[j].x-e[i].x, dy = e[j].y-e[i].y;
                px = e[i].x-dx, py = e[i].y-dy; 
                if (cx+(ret-2)*dx>R) break; 
                // 假设后面每间隔都有一个点都无法达到已有的最大值,由于排序中x是绝对非递减的,所以直接break 
                if (cy+(ret-2)*dy<1 || cy+(ret-2)*dy>C) continue; 
                // 同理,只不过y的值并没有从小到大的规律 
                if (judge(px, py)) continue;
                // 如果计算出来的点在内部,这个点如果没有覆盖就不能走,如果覆盖了那么这条路径就已经寻找过 
                while (judge(cx+dx, cy+dy) && G[cx+dx][cy+dy]) {
                    ++length;
                    cx += dx, cy += dy;
                }
                if (!judge(cx+dx, cy+dy)) { // 落在外面
                    ret = max(ret, length);
                }
            }
        }
        return ret >= 3 ? ret : 0;
    }
    
    int main() {
        while (scanf("%d %d", &R, &C) == 2) {
            memset(G, 0, sizeof (G));
            scanf("%d", &N);
            for (int i = 1; i <= N; ++i) {
                scanf("%d %d", &e[i].x, &e[i].y);
                G[e[i].x][e[i].y] = 1;
            }
            sort(e+1, e+1+N, cmp); // 按照cmp排序后,能够保证所有路径的前两个点一定能够枚举到 
            printf("%d\n", deal());
        }
        return 0;
    }

    不同与上述的搜索+剪枝,动态规划解法中,dp[i][j]表示从j到i这条路径中最多经过多少个点. 那么有动态规划方程:

    dp[i][j] = max(dp[j][k]) + 1, 其中k满足dist(i, j) = dist(j, k) 且 dir(i, j) = dir(j, k).

    求出这些状态后,我们并不一定会取这些值,因为还要满足一个左端点和右端点都在外部.Hash在此算法中的作用是迅速判断是否存在k. 也即通过增量计算新的节点,然后得到这个节点是否在给定的N个节点中.

    代码如下:

    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #define MOD 10007
    #define MAXN 5005
    using namespace std;
    
    int R, C, N;
    
    struct Node {
        int x, y;
    }e[MAXN];
    
    struct HASHLIST {
        int No, next;
    }T[10010];
    
    int idx, head[10010]; // hash表的头指针 
    short int dp[MAXN][MAXN];
    
    bool cmp(Node a, Node b) {
        if (a.x != b.x) return a.x < b.x;
        else return a.y < b.y;    
    }
    
    int hash(int x, int y) {
        return (x*C+y) % MOD;
    }
    
    void insert(int No, int key) {
        ++idx;
        T[idx].No = No;
        T[idx].next = head[key];
        head[key] = idx;
    }
    
    bool judge(int x, int y) {
        return x>0&&x<=R&&y>0&&y<=C;
    }
    
    int get(int x, int y) {
        int key = hash(x, y);
        for (int i = head[key]; i != -1; i = T[i].next) {
            int No = T[i].No;
            if (e[No].x == x && e[No].y == y) {
                return No;
            }
        }
        return -1;
    }
    
    int DP() {
        int nx, ny, px, py, loc;
        short ret = -1;
        for (int i = 1; i <= N; ++i) { // 枚举路径是从左上到右下角 
            for (int j = 1; j < i; ++j) { // 遍历所有节点的组合情况 
                nx = 2*e[j].x - e[i].x; // 按照i,j之间的长度计算出来的下一个节点位置
                ny = 2*e[j].y - e[i].y;
                if (judge(nx, ny)) {
                    loc = get(nx, ny);
                    if (loc != -1 && dp[j][loc] != -1) {
                        dp[i][j] = dp[j][loc] + 1;
                    } else {
                        dp[i][j] = -1;
                    }
                } else { // 表示直接跳出去了 
                    dp[i][j] = 2;
                }
                px = 2*e[i].x - e[j].x;
                py = 2*e[i].y - e[j].y;
                if (!judge(px, py)) {
                    ret = max(ret, dp[i][j]);
                }
            }
        }
        return ret >= 3 ? ret : 0;
    }
     
    int main() {
        while (scanf("%d %d", &R, &C) == 2) {
            idx = -1;
            memset(head, 0xff, sizeof (head));
            scanf("%d", &N);
            for (int i = 1; i <= N; ++i) {
                scanf("%d %d", &e[i].x, &e[i].y);
            }
            sort(e+1, e+1+N, cmp);
            for (int i = 1; i <= N; ++i) {
                insert(i, hash(e[i].x, e[i].y)); // 将这个键值插入到hash表中
            }
            printf("%hd\n", DP());
        }
        return 0;    
    } 
  • 相关阅读:
    总结对象和数组存储东东的缺点和优点
    this关键字
    修改对象属性的方法
    调用对象属性的两种方式
    数组可储存的东西
    对比字面量和结构函数创建对象的相同之处和不同之处
    构造函数创建对象
    字面量创建对象
    SQL语句
    使用SQL Server 2008的事务日志传送功能备份数据库(logshiping)
  • 原文地址:https://www.cnblogs.com/Lyush/p/2858045.html
Copyright © 2020-2023  润新知