2024年7月

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script><style type="text/css">em{font-size: large;font-weight: bold;}</style>

洛谷P2324 [SCOI2005] 骑士精神

题目在P2324 [SCOI2005] 骑士精神

初步分析

输入棋盘当前的状态,要求出从当前状态转移到目标状态所需要的最少变换次数。将每种状态作为无向图的一个节点,能通过走“马”字相互转换的两种状态对应图上的两个相邻节点。

此时问题就转变成求从某结点A到目标节点B的最短路径的长度。

棋盘的状态不同,即棋盘上黑白子的位置、空白的位置不同;棋盘不同状态的转换通过能移动到空白处的棋子移动(改变位置)完成。

算法选用

假设大师在这题会用IDA*。
  • 棋盘的状态非常多,而题目有15的路径长度限制,饱学之士将能使用启发式搜索进行剪枝。对于图中的任意点M,经过该点的路径长\(l=AM+MB \leq 15\)。从始点开始绘制路径到\(M\)时\(MA\)已经有值,则在搜索树上剪去\(MB>15-AM\)的结点。

    可以不严谨地证明当棋盘在M状态下有n个位置的状态不同时,最少需要n-1步来达到相同,计算两个状态间不同的个数为\(n = h(M,B)\) ,则在搜索树上剪去\(n-1+AM \geq 15\)的结点。这是启发式搜索的估值函数

  • 常用求最短路径的广度优先搜索由于不好储存节点状态(可能超内存)而不能实行,深度优先搜索由于连续两次搜索的状态相邻可以避免这一问题。为避免深度过大超时需要最大深度常量,在递归函数中能访问,与当前深度做判断是否剪枝

    一般使用此方法会让深度递增到最大值,每个深度都重新搜索,叫限制深度的(迭代加深)深度优先搜索

代码实现

  1. 创建一些全局量
  2. 这题的终点是个常量。走“马”也很常规。

    //最后目标
    int goal[5][5] = {
    {1,1,1,1,1},
    {0,1,1,1,1},
    {0,0,2,1,1},
    {0,0,0,0,1},
    {0,0,0,0,0}
    };
    //用于走马
    int dx[8] = { 1,1,2,2,-1,-1,-2,-2 };
    //用于走马
    int dy[8] = { 2,-2,1,-1,2,-2,1,-1 };

  3. 输入储存数据
  4. 没有什么特别,注意输入数据的字符中间没有空格。

    char c;
    for (int i = 0; i < 5; ++i)
    {
    for (int j = 0; j < 5; ++j)
    {
    cin >> c;
    if (c == '*') {
    blank[0] = i;
    blank[1] = j;
    chessboard[i][j] = 2;
    }
    else chessboard[i][j] = c - '0';
    }
    }//输入了棋盘

  5. “ID”部分
  6. 终止条件是深度depth、估值函数eval满足\(depth>maxdepth\)、\(eval+depth-1>maxdepth\)回溯;或\(eval=0\)跳出递归,将depth赋给能在main()中被访问的变量。

    这里选择当前节点的空白处位置x和y、棋子位置chessboard(放在了全局)、当前深度depth和本轮的最大深度maxdepth作为递归dfs()的参数。

    //储存棋盘
    int chessboard[5][5];
    //标记是否成功了
    bool succ = false;
    //限制深度的深搜
    void dfs(int depth,int x,int y, int maxdepth) {
    if (depth > maxdepth) {
    return;
    }//步数超过了限制的最大步数
    int count = eval();
    if (count == 0) {
    succ = true;
    return;
    }//达成目标
    if (depth + count - 1 > maxdepth)
    {
    return;
    }//未来不能满足条件了

    for (int i = 0; i lt; 8; ++i) {
    int a = x + dx[i], b = y + dy[i];
    if (0 <= a && a < 5 && 0 <= b && b < 5) {
    swap(chessboard[a][b], chessboard[x][y]);
    dfs(depth + 1, a, b, maxdepth);
    if (succ)return;
    swap(chessboard[a][b], chessboard[x][y]);//回溯
    }
    }
    }
    int main(){
    ...
    int ans = -1;
    for (int i = 1; i <= 15; ++i) {
    dfs(0, x, y, i);
    if (succ) {
    ans = i;
    break;
    }
    }
    cout << ans << endl;
    ...
    }

  7. 估值函数
  8. 根据前面的分析,估值函数只需要统计两个状态的不同处的个数并返回就可以。

    //估值函数
    int eval() {
    int ans = 0;
    for (int i = 0; i < 5; ++i) {
    for (int j = 0; j < 5; ++j) {
    if (chessboard[i][j] != goal[i][j]) {
    ans++;
    }
    }
    }
    return ans;
    }

最终代码

#include<iostream>

using namespace std;
//储存棋盘
int chessboard[5][5];
//最后目标
int goal[5][5] = {
{1,1,1,1,1},
{0,1,1,1,1},
{0,0,2,1,1},
{0,0,0,0,1},
{0,0,0,0,0}
};
//用于走马
int dx[8] = { 1,1,2,2,-1,-1,-2,-2 };
//用于走马
int dy[8] = { 2,-2,1,-1,2,-2,1,-1 };
//空白坐标
int blank[2];
//标记是否成功了
bool succ = false;

//估值函数
int eval() {
int ans = 0;
for (int i = 0; i < 5; ++i) {
for (int j = 0; j < 5; ++j) {
if (chessboard[i][j] != goal[i][j]) {
ans++;
}
}
}
return ans;
}

//限制深度的深搜
void dfs(int depth,int x,int y, int maxdepth) {
//if (maxdepth == 7)cout << depth << endl;
if (depth > maxdepth) {
return;
}//步数超过了限制的最大步数
int count = eval();
if (count == 0) {
succ = true;
return;
}//达成目标
if (depth + count - 1 > maxdepth)
{
return;
}//未来不能满足条件了

for (int i = 0; i < 8; ++i) {
int a = x + dx[i], b = y + dy[i];
if (0 <= a && a < 5 && 0 <= b && b < 5) {
swap(chessboard[a][b], chessboard[x][y]);
dfs(depth + 1, a, b, maxdepth);
if (succ)return;
swap(chessboard[a][b], chessboard[x][y]);//回溯
}
}
}

int main() {
int t; cin >> t;
while (t--)
{
succ = false;
char c;
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < 5; ++j)
{
cin >> c;
if (c == '*') {
blank[0] = i;
blank[1] = j;
chessboard[i][j] = 2;
}
else chessboard[i][j] = c - '0';
}
}//输入了棋盘

int ans = -1;
for (int i = 1; i <= 15; ++i) {
dfs(0, blank[0], blank[1], i);
if (succ) {
ans = i;
break;
}
}
cout << ans << endl;
}
}

附加内容

对于没见过迭代加深DFS的菜的人,可以通过构造一个小的数据结构来储存状态,避免超出内存。使用广度优先搜索和A*。

广度优先搜索中,对每个结点的搜索(直接写在了main()中)选用描述当前棋盘状态的board、当前深度depth作为隐式的参数,最大深度15可以看作常量参数。由于难以回溯而采取用队列queue<pack>储存信息的方式。

添加该状态的前一个状态的空白处坐标在pack结构内,可以减去重复枝;应该也可以用unordered_map来储存搜索过的所有状态来减少重复。代码使用了priority_queue来提速。

#include<iostream>
#include<bitset>
#include<queue>
#include<chrono>

using namespace std;
typedef bitset<25> bs;

//用于走马
int dx[8] = { 1,1,2,2,-1,-1,-2,-2 };
//用于走马
int dy[8] = { 2,-2,1,-1,2,-2,1,-1 };

//记录棋盘状态
struct chessboard {
bs pieces;
int blank;
//空白位置的坐标
const pair cdntBlank() const {
return make_pair(blank / 5, blank % 5);
}
//将(x,y)转换成pos
static int castPos(const pair& p) {
if (0 <= p.first && p.first < 5 && 0 <= p.second && p.second < 5) {
return p.first * 5 + p.second;
}
else return -1;
}
};
const chessboard goal = { 549855 ,12 };

//返回移动后的棋盘状态
chessboard moveReturn(const chessboard& c, int pos) {
bs temp = c.pieces;
temp[c.blank] = c.pieces[pos];
temp[pos] = 0;
return chessboard{ move(temp) ,pos };
}

//估值函数
int evaluateDistance(const chessboard& crt_board, const chessboard& goal) {
int ans = 0;
for (int i = 0; i < 25; ++i) {
ans = (crt_board.pieces ^ goal.pieces).count();
}
if (!crt_board.pieces[goal.blank])ans++;
if (!goal.pieces[crt_board.blank])ans++;
if (goal.blank == crt_board.blank)ans -= 2;
return ans;
}

//用于队列的类
struct pack
{
chessboard board;
int last_blank;
int depth;
int eval;
class less {
public:
int operator()(const pack& a, const pack& b) {
return a.depth + a.eval > b.depth + b.eval;
}
};
};

//queue bfs_queue;

int main() {
int t; cin >> t;
const int MAXDEPTH = 15;
while (t--)
{
chessboard board{ 0,0 };
bs temppieces;
for (int i = 0; i < 25; ++i) {
static char c;
cin >> c;
if (c == '1')temppieces[i] = 1;
else if (c == '*')board.blank = i;
//else temppieces[i] = 0;
}
board.pieces = move(temppieces);//输入棋盘

//bfs_queue = queue();
int ans = -1;
priority_queue,pack::less> bfs_queue;
bfs_queue.push({ board,-1,0,evaluateDistance(board,goal) });
int pos;
do
{
pack front = bfs_queue.top();
bfs_queue.pop();

if (front.eval == 0) {
ans = front.depth;
break;
}
else if (front.depth >= MAXDEPTH) {
continue;
}
if (front.depth + front.eval - 1 > MAXDEPTH) {
continue;
}

auto x_y = front.board.cdntBlank();
int cx_y = chessboard::castPos(x_y);
for (int i = 0; i < 8; ++i) {
pos = chessboard::castPos(make_pair(x_y.first + dx[i], x_y.second + dy[i]));
if (pos == front.last_blank)continue;
if (pos != -1) {
chessboard newboard = move(moveReturn(front.board, pos));
bfs_queue.push({ newboard,
cx_y,
front.depth + 1,
evaluateDistance(newboard,goal) });
}
}
} while (!bfs_queue.empty());
cout << ans << endl;
}
}

第二周周报

提交时间

2024/07/13

本周解决的问题

图的搜索算法

高速路网项目

遇到的困难

1.剪枝和启发式搜索的trick太难想了

2.高速路网调试挺难调

解决方法:

1.多做题积累灵感

2.一些#define DEBUG之类的输出调试方法,和一些gdb的断点调试方法

收获

1.更熟悉了一点gdb的操作

2.复习了git的操作

3.实操补充实现了一个小项目(高速路网)

经验

1.跑这个项目的调试可能还是得熟悉一下linux的相关操作

2.分工要合理 比如谁来写输出调试信息的接口 谁写核心算法 谁进行项目重构 不要像我一样在开发的途中写一堆debug输出

3.c语言实现这个项目的话感觉挺麻烦的...

对本周课程安排 意见 | 建议

暂无

对助教管理方式&教学方式 意见 | 建议

暂无

其他

暂无

这里什么都没有
多来点项目实战啊~没有项目实战我要似了(逃
喜欢做项目 喜欢写算法 是这样的
希望不要被采纳了,不然学弟学妹们就喷我了
为啥感觉还是996适合我(被暴打

高速路网-爱来自第三学期

这是一个项目题。

题目描述

2024-07-12T07:28:47.png 2024-07-12T07:28:57.png 2024-07-12T07:29:09.png 2024-07-12T07:29:18.png

题意分析

分为2个部分: 1.将图片转换为Graph 2.对Graph进行次短路算法

实现思路:

1.将图片转换为Graph

1.对读取的PNG进行广度优先染色,相同颜色的标一个id,并将每一个州的中心点记录 2.对染色后的地图的每一个州进行六个方向的查询,进行建边

2.次短路

使用迷阵题的代码,注意如果有多条最小路径,不能输出最小路径,要输出次小路径。 使用优先队列优化时间。

代码:

小学期结束后填坑。

https://se.jisuanke.com/_996/graph_lab(已填坑)

#include "state.h"
#include <queue>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <set>
void init_State(struct State *s)
{
    s->industry = nullptr;
    s->pre = nullptr;
    s->count = 0;
    s->cord.push_back({0, 0});
    s->mind = 0;
    s->Edge.clear();
    // TODO
}
void delete_State(struct State *s)
{
    // TODO
    delete[] s->industry;
    delete[] s->pre;
    std::vector<std::vector<edge>>().swap(s->Edge);
    std::vector<std::pair<int, int>>().swap(s->cord);
}
void parse(struct State *s, struct PNG *p)
{
    // TODO
    // 即统计图片中联通块的个数,或者叫做统计州
    // s中要存所有州的信息,以及他们的相邻关系
    // 便于后续查找最短路径
    // p->image 为读入的位图数据
    // 思路:循环对每个结点进行编号
    // 对每个结点进行6个方向的循环加边
    // p->image max_size 5000*5000

    // -1代表白色 -2代表黑色 1-n代表该像素块属于的编号
    int *ids = new int[p->width * p->height]();

    int id = 1;
    for (int y = 0; y < p->height; y++)
    {
        for (int x = 0; x < p->width; x++)
        {
            if (ids[y * p->width + x] == 0)
            {
                PXL *pixel = get_PXL(p, x, y);
                if (pixel->blue == 0 && pixel->green == 0 && pixel->red == 0)
                { // black
                    int tmpx, tmpy;
                    color(p, ids, -1, x, y, tmpx, tmpy);
                }
                else if ((pixel->red == 255 && pixel->blue == 255 && pixel->green == 255))
                { // white
                    int tmpx, tmpy;
                    color(p, ids, -2, x, y, tmpx, tmpy);
                }
                else
                { // 正常格子
                    int tmpx, tmpy;
                    color(p, ids, id, x, y, tmpx, tmpy);
                    s->cord.push_back({tmpx, tmpy});
                    id++;
                }
            }
        }
    }
    // 染色标记完毕

    // 开始记录每个州的发达程度industry
    s->count = id - 1;
    s->industry = new int[s->count + 1]();
    s->Edge.resize(s->count + 1);
    for (int y = 0; y < p->height; y++)
    {
        for (int x = 0; x < p->width; x++)
        {
            if (ids[y * p->width + x] != -1 && ids[y * p->width + x] != -2)
            {
                int _id = get_id(ids, x, y, p->width, p->height);
                if (s->industry[_id] != 0)
                {
                    continue;
                }
                PXL *pixel = get_PXL(p, x, y);
                s->industry[_id] = cal_industry(pixel);
            }
        }
    }

    // 对每个州进行六方向连接 建边  标记某个顶点是否已经扩展好边
    int dir[6][2] = {
        {-8, 0}, {8, 0}, {-4, -8}, {4, -8}, {-4, 8}, {4, 8}};
    for (size_t i = 1; i <= s->count; i++)
    {
        std::pair<int, int> cord = s->cord[i];
        for (int j = 0; j < 6; j++)
        {
            int tx = cord.first + dir[j][0];
            int ty = cord.second + dir[j][1];

            if (in(tx, ty, p->width, p->height))
            {
                int toid = get_id(ids, tx, ty, p->width, p->height);
                if (toid != 0 && toid != -1 && toid != -2)
                {
                    s->Edge[i].push_back({toid, s->industry[toid], 1});
                }
            }
        }
    }

#ifdef DEBUG
    for (int i = 1; i <= s->count; i++)
    {
        printf("(%d,%d) ", s->cord[i].first, s->cord[i].second);
    }
    puts("");
#endif // 打印每个州的中心点

#ifdef DEBUG
    int cnt = 0;
    for (int i = 1; i <= s->count; i++)
    {
        for (edge node : s->Edge[i])
        {
            printf("%d %d %d\n ", i, node.v, node.w);
            cnt++;
        }
    }
    printf("%d %d\n", s->count, cnt);
#endif // 打印每个顶点的邻接表

#ifdef DEBUG
    for (int i = 1; i <= s->count; i++)
    {
        std::cout << s->industry[i] << " ";
    }
    puts("");
#endif // 打印每个州的发达程度

#ifdef DEBUG
    for (int i = 0; i < p->height; i++)
    {
        for (int j = 0; j < p->width; j++)
        {
            std::cout << std::setw(5) << ids[i * p->width + j] << " ";
        }
        puts("");
    }
#endif // 打印染色后的图

    delete[] ids;
}

int solve1(struct State *s)
{
    int ans = dij2(s, 1);
    return ans;
}

int solve2(struct State *s)
{
    // TODO
    if (s->pre == nullptr)
    {
        solve1(s);
    }
    int *path = new int[s->count + 1]();
    int pathid = 0;
    getpath(path, s->count, pathid, s->pre);
#ifdef DEBUG_PATH
    printf("----This is Shortest PATH----\n");
    for (int i = 0; i < pathid; i++)
    {
        printf("%d", path[i]);
        if (i != pathid - 1)
        {
            printf("->");
        }
    }
    puts("");
    printf("-----------------------------\n");
#endif
    long long ans = 0x3f3f3f3f;
    for (int i = 0; i < pathid - 1; i++)
    {
        int u = path[i];
        int v = path[i + 1];
        for (edge &e : s->Edge[u])
        {
            if (e.v == v)
            {
                e.f = 0;
            }
        }

        // int *pre = nullptr;
        long long tmp = dij2(s, 0); // ans
        // int *tmppath = new int[s->count + 1]();
        // int tmpid = 0;
        // getpath(tmppath, s->count, tmpid, pre);
#ifdef DEBUG_TMP_PATH
        printf("----This is the Second Shortest PATH----\n");
        for (int i = 0; i < tmpid; i++)
        {
            printf("%d", tmppath[i]);
            if (i != tmpid - 1)
            {
                printf("->");
            }
        }
        printf(" value:%d",tmp);
        puts("");
        printf("-----------------------------\n");
#endif
        // delete[] tmppath;
        if (ans > tmp)
        {
            if(s->mind&&tmp>s->mind)
            {
                ans = tmp;
            }
        }
        for (edge &e : s->Edge[u])
        {
            if (e.v == v)
            {
                e.f = 1;
            }
        }
    }
    delete[] path;
    return ans;
}

// long long dij3(struct State *s, int *(&pre))
// {
//     if (pre == nullptr)
//     {
//         pre = new int[s->count + 1]();
//     }
//     struct node
//     {
//         long long dis, u;

//         bool operator>(const node &a) const { return dis > a.dis; }
//     };
//     const long long inf = 0x3f3f3f3f;
//     long long *dis = new long long[s->count + 1];
//     int *vis = new int[s->count + 1]();
//     std::priority_queue<node, std::vector<node>, std::greater<node>> q;
//     for (size_t i = 0; i < s->count + 1; i++)
//     {
//         dis[i] = inf;
//     }
//     dis[1] = 0;
//     q.push({0, 1});
//     while (!q.empty())
//     {
//         int u = q.top().u;
//         q.pop();
//         if (vis[u])
//             continue;
//         vis[u] = 1;
//         for (auto ed : s->Edge[u])
//         {
//             int v = ed.v, w = ed.w;
//             if (ed.f)
//             {
//                 if (dis[v] > dis[u] + w)
//                 {
//                     dis[v] = dis[u] + w;

//                     pre[v] = u;
// #ifdef DEBUG_DIJPATH
//                     printf("%d->%d:%d\n", u, v, w);
// #endif

//                     q.push({dis[v], v});
//                 }
//             }
//         }
//     }
// #ifdef DEBUG_1
//     for (int i = 0; i < s->count; i++)
//     {
//         printf("pre[%d]:%d ", i, s->pre[i]);
//     }
// #endif
//     long long ans = dis[s->count];
//     delete[] vis;
//     delete[] dis;
//     return ans;
// }

long long dij2(struct State *s, int opt)
{
    if (s->pre == nullptr && opt)
    {
        s->pre = new int[s->count + 1]();
    }
    struct node
    {
        long long dis, u;

        bool operator>(const node &a) const { return dis > a.dis; }
    };
    const long long inf = 0x3f3f3f3f;
    long long *dis = new long long[s->count + 1];
    int *vis = new int[s->count + 1]();
    std::priority_queue<node, std::vector<node>, std::greater<node>> q;
    for (size_t i = 0; i < s->count + 1; i++)
    {
        dis[i] = inf;
    }
    dis[1] = 0;
    q.push({0, 1});
    while (!q.empty())
    {
        int u = q.top().u;
        q.pop();
        if (vis[u])
            continue;
        vis[u] = 1;
        for (auto ed : s->Edge[u])
        {
            int v = ed.v, w = ed.w;
            if (ed.f)
            {
                if (dis[v] > dis[u] + w)
                {
                    dis[v] = dis[u] + w;
                    if (opt)
                    {
                        s->pre[v] = u;
#ifdef DEBUG_DIJPATH
                        printf("%d->%d:%d\n", u, v, w);
#endif
                    }
                    q.push({dis[v], v});
                }
            }
        }
    }
#ifdef DEBUG_1
    for (int i = 0; i < s->count; i++)
    {
        printf("pre[%d]:%d ", i, s->pre[i]);
    }
#endif
    long long ans = dis[s->count];
    if(opt)
    {
        s->mind = ans;
    }
    delete[] vis;
    delete[] dis;
    return ans;
}

/// @brief 获取路径
/// @param path 路径存入path
/// @param x 从x往前推
/// @param pathid path大小存入pathid
/// @param pre 前驱结点数组
void getpath(int *path, int x, int &pathid, int *pre)
{
    if(pre==nullptr)
    {
        return;
    }
    if (x == 1)
    {
        path[pathid++] = x;
        return;
    }
    getpath(path, pre[x], pathid, pre);
    path[pathid++] = x;
}

inline bool in(int x, int y, int width, int height)
{
    return x >= 0 && x < width && y >= 0 && y < height;
}

// aid function
// 对图进行染色 用来标记id
void color(struct PNG *p, int *map, const int id, const int sx, const int sy, int &center_x, int &center_y)
{
    int dir[4][2] = {{1, 0}, {0, -1}, {-1, 0}, {0, 1}};
    int max_x = -1, max_y = -1;
    int min_x = INT_MAX, min_y = INT_MAX;
    std::queue<std::pair<int, int>> q;
    q.push({sx, sy});
    PXL *origin_pxl = get_PXL(p, sx, sy);
    map[sy * p->width + sx] = id;
    while (!q.empty())
    {
        std::pair<int, int> coordinate = q.front();
        q.pop();

        max_x = max(max_x, coordinate.first);
        max_y = max(max_y, coordinate.second);
        min_x = min(min_x, coordinate.first);
        min_y = min(min_y, coordinate.second);

        for (int i = 0; i < 4; i++)
        {
            int tx = coordinate.first + dir[i][0];
            int ty = coordinate.second + dir[i][1];
            if (in(tx, ty, p->width, p->height))
            {
                PXL *pixel = get_PXL(p, tx, ty);
                if ((*pixel == *origin_pxl) && map[ty * p->width + tx] == 0)
                {
                    map[ty * p->width + tx] = id;
                    q.push({tx, ty});
                }
            }
        }
    }
    center_x = (max_x + min_x) / 2;
    center_y = (max_y + min_y) / 2;
}

int get_id(int *ids, int x, int y, int width, int height)
{
    if (!in(x, y, width, height))
    {
        return 0;
    }
    return ids[y * width + x];
}

int cal_industry(PXL *pxl)
{
    return (255 * 255 * 3 - pxl->red * pxl->red - pxl->green * pxl->green - pxl->blue * pxl->blue);
}

int max(int a, int b)
{
    if (a > b)
    {
        return a;
    }
    return b;
}

int min(int a, int b)
{
    if (a < b)
    {
        return a;
    }
    return b;
}

2024-07-10T12:35:30.png

2024-07-10T12:35:43.png

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "io.h"
#define MAXN 20

char map[MAXN][MAXN], vis[MAXN][MAXN][MAXN * MAXN][4];
int k;
int ex, ey;
int n, m;
int dir[4][2] = {{1, 0}, {0, -1}, {-1, 0}, {0, 1}};

int in(int x, int y)
{
    return (x >= 0 && x < n && y >= 0 && y < m);
}
char visnake[10];
int ans = MAXN * MAXN * 10;
int sx, sy;
// walk
int tmp = 0;
void walk(char tmpmap[MAXN][MAXN], int x, int y, int tox, int toy)
{
    if (tmpmap[tox][toy] == '0' + k)
    {
        tmp = tmpmap[tox][toy] - '0';
    }
    tmpmap[tox][toy] = tmpmap[x][y];
    if (tmpmap[x][y] == '0' + k)
    {
        tmpmap[x][y] = '.';
    }
    if ((tmp) && (tmpmap[tox][toy] == tmp - 1 + '0'))
    {
        tmpmap[x][y] = tmp + '0';
    }
    int nx = x, ny = y;
    for (int i = 0; i < 4; i++)
    {
        int tx = x + dir[i][0];
        int ty = y + dir[i][1];
        if (tmpmap[tx][ty] == tmpmap[x][y] + 1)
        {
            visnake[tmpmap[tx][ty] - '0'] = 1;
            nx = tx, ny = ty;
            break;
        }
    }
    if (nx == x && ny == y)
    {
        return;
    }
    walk(tmpmap, nx, ny, x, y);
}
// update
void update(char tmpmap[MAXN][MAXN], int sx, int sy, int tx, int ty)
{
    tmp = 0;
    memset(visnake, 0, sizeof(visnake));
    walk(tmpmap, sx, sy, tx, ty);
    //disp(tmpmap, n, m); // 每update一次就输出
}

struct node
{
    char map[MAXN][MAXN];
    int x, y;
    int step;
    int dir;
} q[MAXN * MAXN * 10];

struct node make_node(const char map[MAXN][MAXN], int x, int y, int step, int dir)
{
    struct node tmp;
    memcpy(tmp.map, map, MAXN * MAXN);
    tmp.x = x, tmp.y = y;
    tmp.step = step;
    tmp.dir = dir;
    return tmp;
}

int bfs()
{
    int l = 0, r = 0;
    for (int i = 0; i < 4; i++)
    {
        char tmp[MAXN][MAXN];
        memcpy(tmp, map, MAXN * MAXN);
        int tx = sx + dir[i][0];
        int ty = sy + dir[i][1];
        if (in(tx, ty) && !vis[tx][ty][1][i] && ((tmp[tx][ty] == '0' + k&&k!=2) || tmp[tx][ty] == '@' || tmp[tx][ty] == '.'))
        {
            vis[tx][ty][1][i] = 1;
            update(tmp, sx, sy, tx, ty);
            q[r++] = make_node(tmp, tx, ty, 1, i);
        }
    }
    while (l < r)
    {
        struct node tmp = q[l++];
        char tmpmap[MAXN][MAXN];
        memcpy(tmpmap, tmp.map, MAXN * MAXN);
        for (int i = 0; i < 4; i++)
        {
            memcpy(tmpmap, tmp.map, MAXN * MAXN);
            int tx = tmp.x + dir[i][0], ty = tmp.y + dir[i][1];
            if (in(tx, ty) && !vis[tx][ty][tmp.step + 1][i] && (((tmpmap[tx][ty] == '0' + k&&k!=2)) || (tmpmap[tx][ty] == '@') || (tmpmap[tx][ty] == '.')))
            {
                if(tmpmap[tx][ty]=='@')
                {
                    return tmp.step+1;
                }
                vis[tx][ty][tmp.step + 1][i] = 1;
                update(tmpmap, tmp.x, tmp.y, tx, ty);
                q[r++] = make_node(tmpmap, tx, ty, tmp.step + 1, i);
            }
        }
    }
    return -1;
}

void user_control()
{
    //disp(map, n, m);
    int ch;

    while (scanf("%d", &ch))
    {
        switch (ch)
        {
        case 2:
            // down
            update(map, sx, sy, sx + 1, sy);
            sx++;
            break;
        case 4:
            // left
            update(map, sx, sy, sx, sy - 1);
            sy--;
            break;
        case 8:
            // up
            update(map, sx, sy, sx - 1, sy);
            sx--;
            break;
        case 6:
            // right
            update(map, sx, sy, sx, sy + 1);
            sy++;
            break;
        default:
            break;
        }
    }
}
#define RUN
int main()
{
    // freopen("out.txt", "w+", stdout);

    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++)
    {
        scanf("%s", map[i]);
    }

    disp(map, n, m);
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            if (map[i][j] >= '1' && map[i][j] <= '9')
            {
                if (map[i][j] - '0' > k)
                {
                    k = map[i][j] - '0';
                }
            }
            if (map[i][j] == '1')
            {
                sx = i, sy = j;
            }
            if (map[i][j] == '@')
            {
                ex = i, ey = j;
            }
        }
    }
#ifdef RUN
    printf("%d", bfs());
#endif
#ifdef DEBUG
    user_control();
#endif
}

正方形 - 计蒜客

2024-07-09T12:29:56.png

题意分析:

搜索4条边,使得4条边满足相等而且等于sum/4,即数组所有元素一起能构成一个正方形

初代算法:

dfs(int id, int l1,int l2,int l3,int l4)
{
    dfs(id+1,l1+p[id],l2,l3,l4);
    dfs(id+1,l1,l2+p[id],l3,l4);
    dfs(id+1,l1,l2,l3+p[id],l4);
    dfs(id+1,l1,l2,l3,l4+p[id]);
}

对4条边进行搜索 会超时

分析:

每条边都是等效的,因此重复状态很多

不是很理解ac代码的k的作用.

#include <stdio.h>
#define MAXN 25
long long p[MAXN], n;
long long sum = 0;
long long a = 0;

int ok = 0;
long long vis[MAXN];
// x表示当前正在搜索第x(0,1,2)条边
// sum 表示当前搜索的边的长度
// k表示//避免重复状况(?)
// 即 若当前dfs的k仍从1开始,
// 遇到一个下标介于1和k之间的下标(记为j),
// 并且发现vis[j]=0,
// 那么其实已经说明j被这层dfs前面的dfs搜索过,
// 而且还不是正确答案状态,
// 可以省略这些状态,
// 所以每层k都要进行+1
void dfs(int sum,int k,int x)
{
    if(ok)
    {
        return;
    }
    if(x==3)
    {
        ok=1;
        return;
    }
    if(sum>a)
    {
        return;
    }
    if(sum==a)
    {
        dfs(0,0,x+1);
    }
    for(int i =k;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            dfs(sum+p[i],i+1,x);
            vis[i]=0;
        }
    }
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &p[i]);
        sum += p[i];
    }
    if(sum%4!=0)
    {
        printf("No");
        return 0; 
    }
    a = sum / 4;

    for(int i = 1;i<=n;i++)
    {
        if(p[i]>a)
        {
            printf("No");
            return 0;
        }
    }
    dfs(0,1,0);
    if (ok)
    {
        printf("Yes");
    }
    else
    {
        printf("No");
    }
}