关于BFS和dijkstra(2019.04.20)

我的BFS板子

struct node{/*略*/};//表示一个状态
std::map<node,bool>vis;//判断每个状态是否已访问过
std::queue<node>q;//BFS扩展队列
//BFS主代码
q.push(start_node); //初始节点入队
vis[start_node] = 1;
while (!q.empty())
{
    node head = q.front(); //去队首元素进行拓展
    q.pop();
    for (int i : directions) //遍历每一种拓展方式
    {
        node new = move(head, i); //求新的状态
        if (!valid(new) || vis[new])
            continue; //如果新状态不合要求,即出界(不合逻辑)或已访问过
        //做题
        q.push(new); //新节点入队
        vis[new] = 1;
    }
}

以上的代码中有很多地方使用了伪代码,或者说,不是我们实际编写的形式。下面给出一个具体实现:

struct node
{
	int x, y;												 //坐标
	int step;												 //到此的步数
	node(int x, int y, int step) : x(x), y(y), step(step) {} //构造函数
};
int n, m;												//迷宫大小
bool vis[max_n][max_m];									//标记是否已访问
bool can_walk[max_n][max_m];							//标记是否可走
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0}; //四个方向
std::queue<node> q;										//BFS队列
bool pan(int x, int y)									//判断(x,y)是否合法
{
	return x >= 1 && x <= n && y >= 1 && y <= m && can_walk[x][y];
}
int bfs(int sx, int sy, int tx, int ty) //从(sx,sy)出发到(tx,ty),返回最少步数,无解则为-1
{
	if (!can_walk[sx][sy]) //如果一开始就在墙里,无解
		return -1;
	q.push(node(sx, sy, 0)); //初始位置入队
	vis[sx][sy] = 1;
	while (!q.empty())
	{
		node head = q.front(); //取队首元素
		q.pop();
		if (head.x == tx && head.y == ty) //如果到达终点
			return head.step;
		for (int i = 0; i < 4; i++)
		{
			int xx = head.x + dx[i], yy = head.y + dy[i]; //尝试向四个方向移动
			if (!pan(xx, yy) || vis[xx][yy])
				continue;
			q.push(node(xx, yy, head.step + 1)); //新节点入队
			vis[xx][yy] = 1;
		}
	}
	return -1; //如果走过所有可行解点了,仍然没到达终点,无解
}

用BFS的思想理解dijkstra

water_lift这个垃圾至今不会SPFA
这里指的是堆优化的版本。
首先起点入队。每次在队列中取最短路长度最小的,对所有从该点出发出边到达的点进行松弛并加入队列。这就是堆优化版dijkstra的算法轮廓。
下面是一个大量使用了STL的dijkstra,很慢,但是能A掉P3371 【模板】单源最短路径(弱化版)

#include<bits/stdc++.h>
#define inf INT_MAX
using namespace std;
int n,m,s;
struct edge{
	int to,dis;
	edge(int a,int b):to(a),dis(b){}
};
vector<edge>edges;
vector<int>G[10003];
void add_edge(int u,int v,int w){
	edges.push_back(edge(v,w));
	G[u].push_back(edges.size()-1);
}
struct node{
	int p,dis;
	node(int a,int b):p(a),dis(b){}
};
bool operator< (node a,node b){
	return a.dis>b.dis;
}
priority_queue<node>q;
int d[10003];
bool u[10003];
int main(){
	cin>>n>>m>>s;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		add_edge(u,v,w);
	}
	fill(d+1,d+n+1,inf);
	d[s]=0;
	q.push(node(s,0));
	while(!q.empty()){
		node x=q.top();
		q.pop();
		//cout<<x.p<<' '<<x.dis<<endl;
		if(u[x.p])continue;
		u[x.p]=1;
		for(int i=0;i<G[x.p].size();i++){
			edge e=edges[G[x.p][i]];
			//cout<<'\t'<<e.to<<' '<<d[e.to]<<"->";
			d[e.to]=min(d[e.to],d[x.p]+e.dis);
			//cout<<d[e.to]<<endl;
			q.push(node(e.to,d[e.to]));
		}
	}
	for(int i=1;i<=n;i++){
		cout<<d[i]<<' ';
	}
}

U64942 【常数PK】#2 单源最短路
在此给出一个神奇的题目,用以测试算法优越性。具体来说,就是按照运行时间得分。具体请见题目内描述。
下面是目前得分最高(9525分)的代码(by __Seniorious__):

// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<cctype>
#include<cstdio>
#include<queue>
const int BUF=8000000;
char Buf[BUF],*buf=Buf;
struct edge{
    int nxt,to,val;
};
edge t[700010];
int fis[200010],n,m,s,cnt,di[200010],s_,e_,c_;
bool is_vis[200010];
inline void add(int _s,int _e,int _c){
    t[++cnt].nxt=fis[s_],fis[s_]=cnt,t[cnt].to=e_,t[cnt].val=c_; 
}
struct node{
    int dis,num;
    inline bool operator<(const node &p)const{
        return dis>p.dis;
    }
};
std::priority_queue<node,std::vector<node> >h;
inline void read(int &x){
    for(x=0;!std::isdigit(*buf);++buf);
    for(;std::isdigit(*buf);x=x*10+*buf-'0',++buf);
}
int main(){
    //freopen("tst","r",stdin);
    fread(buf,1,BUF,stdin);
    read(n),read(m),read(s);
    for(int i=1;i<=m;++i){
        read(s_),read(e_),read(c_);
        add(s_,e_,c_);
    }
    for(int i=1;i<=n;++i)di[i]=2147483647;
    di[s]=0;
    h.push(node{0,s});
    while(!h.empty()){
        node QwQ=h.top();h.pop();
        int qwq=QwQ.num;
        if(is_vis[qwq])continue;
        is_vis[qwq]=1;
        for(int j=fis[qwq];j;j=t[j].nxt){
        	int f=t[j].to;
            if(t[j].val+di[qwq]<di[f]){
            	di[f]=t[j].val+di[qwq];
            	h.push(node{di[f],f});
            }
        }
    }
    for(int i=1;i<=n;++i)printf("%d ",di[i]);
    printf("\n");
    return 0;
}

一些题解

P1451 求细胞数量

算法:BFS

从每个非0格BFS,把遇到的所有非0格都变为0。

#include <bits/stdc++.h>
using namespace std;
int m, n;
char mat[101][101];
struct node
{
    int x, y;
    node(int x, int y) : x(x), y(y) {}
};
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
bool pan(int x, int y)
{
    return x >= 1 && x <= m && y >= 1 && y <= n;
}
int ans;
int main()
{
    cin >> m >> n;
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin>>mat[i][j];
        }
    }
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (mat[i][j] == '0')
                continue;
            ans++;
            queue<node> q;
            q.push(node(i, j));
            mat[i][j] = '0';
            while (!q.empty())
            {
                node h = q.front();
                q.pop();
                for (int k = 0; k < 4; k++)
                {
                    int xx = h.x + dx[k], yy = h.y + dy[k];
                    if (!pan(xx, yy) || mat[xx][yy] == '0')
                        continue;
                    q.push(node(xx, yy));
                    mat[xx][yy] = '0';
                }
            }
        }
    }
    cout << ans;
}

P1443 马的遍历

算法:BFS

从(1, 1)
开始BFS,第一次到达某个点的步数就是最少步数。

#include <bits/stdc++.h>
    using namespace std;
typedef pair<int, int> pos;
int n, m, x, y;
const int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1}, dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};
inline bool ck(int x, int y)
{
    return x >= 1 && x <= n && y >= 1 && y <= m;
}
queue<pos> q;
int d[401][401];
int main()
{
    cin >> n >> m >> x >> y;
    pos h = pos(x, y);
    q.push(h);
    memset(d, -1, sizeof(d));
    d[x][y] = 0;
    while (!q.empty())
    {
        pos i = q.front();
        q.pop();
        for (int r = 0; r < 8; r++)
        {
            int xx = i.first + dx[r], yy = i.second + dy[r];
            if (ck(xx, yy) && d[xx][yy] == -1)
            {
                q.push(pos(xx, yy));
                d[xx][yy] = d[i.first][i.second] + 1;
            }
        }
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cout << left << setw(5) << d[i][j];
        }
        cout << endl;
    }
}

P1162 填涂颜色

算法:DFS, BFS,flood fill

有点毒瘤。从边界往里推,如果是空白就删掉,剩下的就是图形。

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pos;
int n;
int in[31][31], co[31][31];
const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
queue<pos> q;
bool ck(int x, int y)
{
    return x >= 1 && x <= n && y >= 1 && y <= n;
}
void bfs(int x, int y)
{
    pos i = pos(x, y);
    q.push(i);
    while (!q.empty())
    {
        pos p = q.front();
        q.pop();
        co[p.first][p.second] = 1;
        for (int d = 0; d < 4; d++)
        {
            int xx = p.first + dx[d], yy = p.second + dy[d];
            if (ck(xx, yy) && in[xx][yy] == 0 && co[xx][yy] == 0)
            {
                q.push(pos(xx, yy));
            }
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> in[i][j];
        }
    }
    for (int i = 1; i <= n - 1; i++)
    {
        if (in[1][i] == 0 && co[1][i] == 0)
            bfs(1, i);
        if (in[i][n] == 0 && co[i][n] == 0)
            bfs(i, n);
        if (in[n][n - i + 1] == 0 && co[n][n - i + 1] == 0)
            bfs(n, n - i + 1);
        if (in[n - i + 1][1] == 0 && co[n - i + 1][1] == 0)
            bfs(n - i + 1, 1);
    }
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (in[i][j] == 0 && co[i][j] == 0)
                cout << 2;
            else
                cout << in[i][j];
            cout << ' ';
        }
        cout << endl;
    }
}

P1506 拯救oibh总部

算法:DFS, BFS,flood fill

思路和上题一致。从边界往里推,遇到空白格子就删掉,最后数没被删掉的空白格子。

#include <bits/stdc++.h>
using namespace std;
int n, m;
bool wall[501][501];
bool vis[501][501];
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
struct node
{
    int x, y;
    node(int x, int y) : x(x), y(y) {}
};
bool valid(int x, int y)
{
    return x >= 1 && x <= n & y >= 1 && y <= m && !wall[x][y] && !vis[x][y];
}
void bfs(node s)
{
    queue<node> q;
    q.push(s);
    vis[s.x][s.y] = 1;
    while (!q.empty())
    {
        node head = q.front();
        q.pop();
        for (int i = 0; i < 4; i++)
        {
            int xx = head.x + dx[i], yy = head.y + dy[i];
            if (!valid(xx, yy))
                continue;
            q.push(node(xx, yy));
            vis[xx][yy] = 1;
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            char c;
            cin >> c;
            if (c == '0')
                wall[i][j] = 0;
            else
                wall[i][j] = 1;
        }
    }
    for (int i = 1; i <= m - 1; i++)
    {
        if (valid(1, i))
            bfs(node(1, i));
        if (valid(n, m - i + 1))
            bfs(node(n, m - i + 1));
    }
    for (int i = 1; i <= n - 1; i++)
    {
        if (valid(i, m))
            bfs(node(i, m));
        if (valid(n - i + 1, 1))
            bfs(node(n - i + 1, 1));
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (valid(i, j))
                ans++;
        }
    }
    cout << ans << endl;
}

P1144 最短路计数

算法:dijkstra

跑dijkstra即可。关于计数,就是如果新方案和原方案的长度一样,那么就累加。累加时注意不能只加一,要把上一个节点的值都加上(不理解的可以看代码)。

#include <bits/stdc++.h>
using namespace std;
int n, m;
const int nmax = 1000001, mmax = 2000001, mod = 100003;
int fir[nmax], to[mmax], nxt[mmax], ecnt;
void add_edge(int u, int v)
{
    to[++ecnt] = v;
    nxt[ecnt] = fir[u];
    fir[u] = ecnt;
}
bool vis[nmax];
int dis[nmax];
int cnt[nmax];
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x, y;
        cin >> x >> y;
        add_edge(x, y);
        add_edge(y, x);
    }
    memset(dis, -1, sizeof(dis));
    queue<int> q;
    cnt[1] = 1;
    dis[1] = 0;
    q.push(1);
    while (!q.empty())
    {
        int h = q.front();
        q.pop();
        if (vis[h])
            continue;
        vis[h] = 1;
        for (int e = fir[h]; e; e = nxt[e])
        {
            if (vis[to[e]])
                continue;
            if (dis[to[e]] == -1)
            {
                dis[to[e]] = dis[h] + 1;
                cnt[to[e]] = cnt[h] % mod;
            }
            else
            {
                if (dis[h] + 1 > dis[to[e]])
                    continue;
                else if (dis[h] + 1 == dis[to[e]])
                    cnt[to[e]] = (cnt[to[e]] + cnt[h]) % mod;
                else
                    dis[to[e]] = dis[h] + 1, cnt[to[e]]++;
            }
            q.push(to[e]);
        }
    }
    for (int i = 1; i <= n; i++)
    {
        cout << cnt[i] << '\n';
    }
}

U68862 _GC滑迷宫(其实数据还是很蒻

算法:BFS

仍按迷宫问题思路即可,注意拓展时要while到墙。

#include <bits/stdc++.h>
using namespace std;
int n, m, sx, sy;
bool mat[501][501];
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
struct pos
{
    int x, y, step;
    pos(int x, int y, int step) : x(x), y(y), step(step) {}
};
queue<pos> q;
bool vis[501][501];
inline bool pan(int x, int y)
{
    return x >= 1 && x <= n && y >= 1 && y <= m && mat[x][y] == 0;
}
bool out(int x, int y)
{
    return (x == 1 || x == n || y == 1 || y == m) && mat[x][y] == 0;
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            cin >> mat[i][j];
        }
    }
    cin >> sx >> sy;
    if(mat[sx][sy]){
        cout<<-1;
        return 0;
    }
    if(out(sx,sy)){
        cout<<0;
        return 0;
    }
    q.push(pos(sx, sy, 0));
    vis[sx][sy] = 1;
    while (!q.empty())
    {
        pos h = q.front();
        q.pop();
        // cout << h.x << ' ' << h.y << endl;
        if (out(h.x, h.y))
        {
            cout << h.step - 1;
            return 0;
        }
        for (int i = 0; i < 4; i++)
        {
            int xx = h.x, yy = h.y;
            while (pan(xx + dx[i], yy + dy[i]))
            {
                xx += dx[i];
                yy += dy[i];
            }
            if ((xx == h.x && yy == h.y) || vis[xx][yy])
                continue;
            vis[xx][yy] = 1;
            q.push(pos(xx, yy, h.step + 1));
        }
    }
    cout << -1;
}

UVA532 Dungeon Master

算法:BFS

三维迷宫。

#include <bits/stdc++.h>
using namespace std;
int l, n, m;
struct pos
{
    int z, x, y, step;
    pos(int z, int x, int y, int step) : z(z), x(x), y(y), step(step) {}
};
int sz, sx, sy;
int tz, tx, ty;
bool mat[31][31][31];
bool vis[31][31][31];
const int dz[6] = {1, 0, 0, -1, 0, 0},
          dx[6] = {0, 1, 0, 0, -1, 0},
          dy[6] = {0, 0, 1, 0, 0, -1};
bool pan(int z, int x, int y)
{
    return z >= 1 && z <= l && x >= 1 && x <= n && y >= 1 && y <= m && mat[z][x][y] == 0;
}
int main()
{
    while (true)
    {
        cin >> l >> n >> m;
        if (l == 0 && n == 0 && m == 0)
            break;
        for (int k = 1; k <= l; k++)
        {
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= m; j++)
                {
                    char c;
                    cin >> c;
                    if (c == '.')
                        mat[k][i][j] = 0;
                    else if (c == '#')
                        mat[k][i][j] = 1;
                    else if (c == 'S')
                    {
                        mat[k][i][j] = 0;
                        sz = k;
                        sx = i;
                        sy = j;
                    }
                    else if (c == 'E')
                    {
                        mat[k][i][j] = 0;
                        tz = k;
                        tx = i;
                        ty = j;
                    }
                    else
                    {
                        cin >> c;
                    }
                    vis[k][i][j] = 0;
                }
            }
        }
        queue<pos> q;
        q.push(pos(sz, sx, sy, 0));
        vis[sz][sx][sy] = 1;
        bool flag = 1;
        while (!q.empty())
        {
            pos head = q.front();
            q.pop();
            if (head.z == tz && head.x == tx && head.y == ty)
            {
                cout << "Escaped in " << head.step << " minute(s).\n";
                flag = 0;
                break;
            }
            for (int i = 0; i < 6; i++)
            {
                int zz = head.z + dz[i], xx = head.x + dx[i], yy = head.y + dy[i];
                if (!pan(zz, xx, yy))
                    continue;
                if (vis[zz][xx][yy])
                    continue;
                q.push(pos(zz, xx, yy, head.step + 1));
                vis[zz][xx][yy] = 1;
            }
        }
        if (flag)
            cout << "Trapped!\n";
    }
}

P1379 八数码难题

算法:BFS

从初始状态开始,做BFS即可,每次转移时枚举空白格子的四个方向。

#include <bits/stdc++.h>
using namespace std;
int n;
const int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};
queue<int> q;
map<int, int> d;
bool valid(int x, int y)
{
    return x >= 0 && x <= 2 && y >= 0 && y <= 2;
}
int main()
{
    cin >> n;
    q.push(n);
    d[n] = 0;
    while (!q.empty())
    {
        int head = q.front();
        q.pop();
        if (head == 123804765)
            break;
        int state[3][3], x, y;
        int f = head;
        for (int i = 2; i >= 0; i--)
        {
            for (int j = 2; j >= 0; j--)
            {
                state[i][j] = f % 10;
                f /= 10;
                if (!state[i][j])
                    x = i, y = j;
            }
        }
        for (int i = 0; i < 4; i++)
        {
            int xx = x + dx[i], yy = y + dy[i];
            int st = 0;
            if (!valid(xx, yy))
                continue;
            swap(state[x][y], state[xx][yy]);
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    st = st * 10 + state[i][j];
                }
            }
            if (!d.count(st))
            {
                d[st] = d[head] + 1;
                q.push(st);
            }
            swap(state[x][y], state[xx][yy]);
        }
    }
    cout << d[123804765] << endl;
}

UVA10603 倒水问题 Fill

算法:BFS or dijkstra

设水杯内水量为状态,倒水为转移,维护答案即可。当然也可以认为是dijkstra。

#include <bits/stdc++.h>
using namespace std;
int t;
struct node
{
    int v[3], dis;
    node(int a, int b, int c, int d)
    {
        v[0] = a;
        v[1] = b;
        v[2] = c;
        dis = d;
    }
};
bool operator<(node x, node y)
{
    return x.dis > y.dis;
}
int c[3], d;
bool vis[201][201];
int dis[201][201];
int ans[201];
int main()
{
    cin >> t;
    while (t--)
    {
        cin >> c[0] >> c[1] >> c[2] >> d;
        memset(vis, 0, sizeof(vis));
        memset(ans, -1, sizeof(ans));
        memset(dis, -1, sizeof(dis));
        priority_queue<node> q;
        q.push(node(0, 0, c[2], 0));
        dis[0][0] = 0;
        vis[0][0] = 1;
        while (!q.empty())
        {
            node head = q.top();
            q.pop();
            // if(vis[head.v[0]][head.v[1]])continue;
            // vis[head.v[0]][head.v[1]]=1;
            for (int i = 0; i < 3; i++)
            {
                if (ans[head.v[i]] == -1)
                    ans[head.v[i]] = head.dis;
                else
                    ans[head.v[i]] = min(ans[head.v[i]], head.dis);
            }
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    if (i == j)
                        continue;
                    int amount = min(head.v[i], c[j] - head.v[j]);
                    node x = head;
                    x.v[i] -= amount;
                    x.v[j] += amount;
                    x.dis += amount;
                    if (vis[x.v[0]][x.v[1]])
                        continue;
                    if (dis[x.v[0]][x.v[1]] == -1 || dis[x.v[0]][x.v[1]] > x.dis)
                    {
                        dis[x.v[0]][x.v[1]] = x.dis;
                        vis[x.v[0]][x.v[1]] = 1;
                        q.push(x);
                    }
                }
            }
        }
        for (; d >= 0; d--)
        {
            if (ans[d] >= 0)
            {
                cout << ans[d] << ' ' << d << endl;
                break;
            }
        }
    }
}
posted @ 2019-04-20 20:41  water_lift  阅读(829)  评论(0编辑  收藏  举报