加载中...

flood fill bfs最后输出路径 dfs(数据分组) stirng.find('x')返回下标+定期重构

flood fill 求连通块

for
for
没标记过 标记 bfs cnt++

bfs()
q[]
st[][]
while(hh<=tt){
if( ) st[][];
}

dfs 重点在于搜索的顺序 找到每个情况
然后在次基础上剪枝

剪枝策略:
1.当方案大于已经最优解
if() return;
if(u==n) ans= ;
2.分组问题 将先放入权重最大的作为一组 更容易减少策略

池塘计数

八方向使用for
for


PII  q[N*N];
char g[N][N];
int n,m;
bool st[N][N];
void bfs(int i,int j){
    
    int tt=0,hh=0;
    q[0]={i,j};
    st[i][j]=true;
    while(hh<=tt){
        auto t=q[hh++];
        for (int i = t.x-1; i <=t.x+1; i ++ ){
            for (int  j= t.y-1; j <=t.y+1 ; j ++ ){
                if(!st[i][j]&&i>=0&&i<n&&j>=0&&j<m&&g[i][j]=='W'){
                    st[i][j]=true;
                    q[++tt]={i,j};
                    
                }
            }
        }
        
    }
    
}
int main()
{
    cin >> n>>m;
    int cnt=0;
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    for (int i = 0; i < n; i ++ ) cin >> g[i];
    for (int i = 0; i < n; i ++ ){
        for (int j = 0; j < m; j ++ ){
            if(g[i][j]=='W'&&!st[i][j]){
                bfs(i,j);cnt++;
            }
        }
    }
    cout << cnt;;
    return 0;
}

城堡问题https://www.acwing.com/problem/content/1100/
能不能走过去 通过点上的二进制表示

void bfs(int sx,int sy){
    PII q[N*N];
    q[0]={sx,sy};
    int tt=0,hh=0;
    int ans=1;
    st[sx][sy]=true;
    while(hh<=tt){
        auto t=q[hh++] ;
        for (int i = 0; i < 4; i ++ ){
            int a=t.x+dx[i],b=t.y+dy[i];
            if(!st[a][b] &&a>=0 &&a<n &&b>=0&&b<m&&!(g[t.x][t.y]>>i&1)){
                st[a][b]=true;
                q[++tt]={a,b};
                ans++;   
            }
        }
    }
    area=max(area, ans);
}

山谷和山峰

判断周围的条件

void bfs(int sx,int sy,bool &has_high,bool &has_low){
    int tt=0,hh=0;
    q[0]={sx,sy};
    st[sx][sy]=true;
    while(hh<=tt){
        auto t= q[hh++];
        for (int i = t.x-1; i <= t.x+1; i ++ ){
            for (int j = t.y-1; j <= t.y+1; j ++ ){
                   if(i<0||i>=n||j<0||j>=n) continue;
                //   if(st[i][j] )  continue;
                   if(g[i][j]!=g[t.x][t.y] ){
                       if(g[i][j]>g[t.x][t.y]) has_high=true;
                       else has_low=true;
                   }else if(!st[i][j]){
                       
                       q[++tt]={i,j};
                       st[i][j]=true;
                    //   cout << i<<" "<<j<<endl;
                   }
            }
        }
    }
    
}
int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ){
        for (int j = 0; j < n; j ++ ){
            cin >> g[i][j];
        }
    }
    int peak=0,valley=0;
    for (int i = 0; i < n; i ++ ){
        for (int j = 0; j < n; j ++ ){
            
            if( !st[i][j] ){
                bool  has_high=false,has_low=false;
                bfs(i,j,has_high,has_low);
            
                if(!has_high) peak++;
            if(!has_low) valley++;//这里是没有才加加 不要搞反了
                
            }
            
            
        }
    }
    cout << peak<<" "<<valley;
    // cout << valley<<" "<<peak;
    return 0;
}

bfs输出路径

#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 1010, M = N * N;

int n;
int g[N][N];
PII q[M];
PII pre[N][N];//[x]为x的坐标 [y]为y的坐标

void bfs(int sx, int sy)
{
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

    int hh = 0, tt = 0;
    q[0] = {sx, sy};

    memset(pre, -1, sizeof pre);

    while (hh <= tt)
    {
        PII t = q[hh ++ ];

        for (int i = 0; i < 4; i ++ )
        {
            int a = t.x + dx[i], b = t.y + dy[i];
            if(a>=0&&a<n&&b>=0&&b<n&&g[a][b]==0&&pre[a][b].x==-1){
                q[++tt]={a,b};
                pre[a][b]=t ;
            }
        }
    }
}

int main()
{
    scanf("%d", &n);

    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            scanf("%d", &g[i][j]);//这里容易错

    bfs(n - 1, n - 1);//从终点开始搜可以省下一个数组

    PII end(0, 0);//从0 0开始输出

    while (true)
    {
        printf("%d %d\n", end.x, end.y);
        if (end.x == n - 1 && end.y == n - 1) break;//到达终点就停止
        end = pre[end.x][end.y];//变成下一个
    }

    return 0;
}


武士风度的牛https://www.acwing.com/problem/content/190/

需要走日字

int bfs(PII start ,PII end){
    int tt=0,hh=0;
    q[0]=start;
  
    memset(d, -1, sizeof d);
      d[start.x][start.y]=0;
    while(hh<=tt){
        auto t=q[hh++];
        // cout << t.x<<" "<<t.y<<endl;
        for (int i = 0; i < 8; i ++ ){
            int a=t.x+dx[i],b=t.y+dy[i];
            
            if(a>=0&&a<n&&b>=0&&b<m&&d[a][b]==-1&&g[a][b]!='*'){
        
                q[++tt]={a,b} ;
                d[a][b]=d[t.x][t.y]+1;
                if(a==end.x&&b==end.y) return d[a][b];
            }
        }
        
    }
    return -1;
}

bfs 求边权为1 的图 距离最小值

一层层的拓展 bf 所有的距离都是最小
类似于dijkstra

int bfs(){
    int tt=0,hh=0;
    q[0]= n;
    memset(d, -1, sizeof d);
    d[n]=0;
    while(hh<=tt){
        int n=q[hh++];
        // cout << n<<" "<<d[n]<<endl;
        if(n==k) return d[n];
        if(n+1<=N &&d[n+1]==-1 ){
            q[++tt]=n+1 ;
            d[n+1]=d[n]+1;
        }
        if(n-1>=0&& d[n-1]==-1){
            q[++tt]=n-1;
            d[n-1]=d[n]+1;
        }
        if(n*2<N &&d[2*n]==-1 ){
            q[++tt]=n*2 ;
            d[2*n]=d[n]+1;
        }
        
    }
    return -1;
}

矩阵距离https://www.acwing.com/problem/content/description/175/

求每个点到最近的1的距离 转化成每个1到其他点的距离


魔板https://www.acwing.com/problem/content/1109/

#include <bits/stdc++.h>
using namespace std;
unordered_map<string, pair<string, char>> pre; // pre存的是当前状态所对应的上一状态的状态和操作

inline string operA(string str) {
    for (int i = 0; i < 4; ++ i)    swap(str[i], str[7 - i]);
    return str;
}

inline string operB(string str) {
    for (int i = 0; i < 3; ++ i) swap(str[2 - i], str[3 - i]), swap(str[4 + i], str[5 + i]);
    return str;
}

inline string operC(string str) {
    swap(str[1], str[2]), swap(str[5], str[6]), swap(str[1], str[5]);
    return str;
}

void bfs(string start, string end) {
    queue<string> que;
    que.push(start);          // 必须从start向end搜索才能保证字典序最小
    while (!que.empty()) {
        string str = que.front();
        que.pop();
        if (str == end) return;
        string move[3];       // 记录下该状态可由三种操作所达到的新状态
        move[0] = operA(str), move[1] = operB(str), move[2] = operC(str);
        for (int i = 0; i < 3; ++ i) {                  // 遍历三种状态
            if (!pre.count(move[i])) {                  // 如果当前状态还没有被记录过
                que.push(move[i]);                      // 将当前状态入队
                pre[move[i]] = make_pair(str, 'A' + i); // 存下当前状态所对应的上一状态的状态和操作 pre 第一个上一个字符串,第二个为操作的值
            }
        }
    }
}

signed main() {
    int x;
    string start = "12345678", end, res;
    for (int i = 0; i < 8; ++ i) {
        scanf("%d", &x);
        end += char(x + '0');
    }

    bfs(start, end);
    while (end != start) {           // 从最终状态向起始状态回溯
        res = pre[end].second + res; // 注意要把前一个操作放在输出序的前面 秒:先放前一个操作 再加当前已经做到的操作
        end = pre[end].first;//end返回上一个字符串
    }
    if (res.length() == 0)  printf("0");
    else   cout<<res.length()<<res;
    return 0;
}


电路维修(双端队列bfs)https://www.acwing.com/problem/content/177/

建立图片

#include <cstring>
#include <iostream>
#include <algorithm>
#include <deque>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 510, M = N * N;

int n, m;
char g[N][N];
int dist[N][N];
bool st[N][N];

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    dist[0][0] = 0;
    deque<PII> q;
    q.push_back({0, 0});

    char cs[] = "\\/\\/";
    int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, 1, -1};//这是点 左上角(x-1,x-1)
    int ix[4] = {-1, -1, 0, 0}, iy[4] = {-1, 0, 0, -1};//图里面不一样(n,m) 往坐上走经过(n-1,m-1) 右上走(n-1,m) 右下走(n,m) 左下走(n,m-1)  

    while (q.size())
    {
        PII t = q.front();
        q.pop_front();

        if (st[t.x][t.y]) continue;
        st[t.x][t.y] = true;//没走过

        for (int i = 0; i < 4; i ++ )//四个方向
        {
            int a = t.x + dx[i], b = t.y + dy[i];//a b是下面一个到达的点
            if (a < 0 || a > n || b < 0 || b > m) continue;

            int ca = t.x + ix[i], cb = t.y + iy[i];//这个ca是找图了
            int d = dist[t.x][t.y] + (g[ca][cb] != cs[i]);//cacb是上一个点到当前点需要经过的路径 

            if (d < dist[a][b])//只有当距离小于的时候即将到达的距离才可以 类似dijktra
            {
                dist[a][b] = d;

                if (g[ca][cb] != cs[i]) q.push_back({a, b});//如果不一样的时候才需要加入 说明距离增加了 所以优先级降低 放入队尾
                else q.push_front({a, b});
            }
        }
    }

    return dist[n][m];
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);//建图

        int t = bfs();

        if (t == 0x3f3f3f3f) puts("NO SOLUTION");//到达的距离是无穷
        else printf("%d\n", t);
    }

    return 0;
}


双向队列广搜

将复杂度从10^10 -> 10^5 次方

#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

/**
 * 这道题的大意就是将A字符串通过一定的规则转变为B字符串
 * 一个朴素的思想就是,把A字符串的所有能转变规则的地方都转变一次
 * 就相当于从A字符串拓展出几条不同的通路来到达一个新的节点,然后依次类推
 * 最后与B字符串连接,这样的问题其实还是一个最短路的问题
 * 但是从题目可以看出字符串长度的上限为20,规则至多为6,且步数最多为10步
 * 那么我们这样暴力的一层一层BFS,每个节点都要被搜到
 * 那么假设每个规则都不重复,那么每个节点都有可能拓展出6条可能的方向来
 * 那么最多遍历的点为6^10 = 60,466,176 ≈ 6*10^7
 * 如果规则还能重复,比如a -> b,a -> c,那么最多需要遍历(20*6)^10 = 120^10这样时间复杂度就更大了
 * --------------------------------------------------------------------------------
 * 所以对于这种时间复杂度很大的最短路的问题,我们需要逆向思考
 * 因为单独从起点开始去寻找终点,这样可能的分支太多了
 * 如果我们起点和终点同时开始去拓展呢,假设10步以内就能找到的话
 * 从起点开始拓展5层,从终点开始拓展5层的时候就能够相遇,这个时候的时间复杂度为2*6^5
 * 时间复杂度就大为减少了
 */ 
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>

using namespace std;

const int N = 6;

int n;
string A, B;
string a[N], b[N];
//q某一端的队列 da现在将要记录的字符串 db用来查询是否在另一端 db变化的规则
int extend(queue<string>& q, unordered_map<string, int>&da, unordered_map<string, int>& db, 
    string a[N], string b[N])
{
    int d = da[q.front()];
    //当队列头部的距离还是d说明还是在这一层
    while (q.size() && da[q.front()] == d)
    {
        auto t = q.front();
        q.pop();

        for (int i = 0; i < n; i ++ )//枚举规则
            for (int j = 0; j < t.size(); j ++ )//枚举当前字符串长度
                if (t.substr(j, a[i].size()) == a[i])
                {
                    string r = t.substr(0, j) + b[i] + t.substr(j + a[i].size());
                    if (db.count(r)) return da[t] + db[r] + 1;
                    if (da.count(r)) continue;
                    da[r] = da[t] + 1;
                    q.push(r);
                }
    }

    return 11;
}

int bfs()
{
    if (A == B) return 0;
    queue<string> qa, qb;
    unordered_map<string, int> da, db;

    qa.push(A), qb.push(B);
    da[A] = db[B] = 0;

    int step = 0;
    while (qa.size() && qb.size())
    {
        int t;
        if (qa.size() < qb.size()) t = extend(qa, da, db, a, b);
        else t = extend(qb, db, da, b, a);

        if (t <= 10) return t;//一旦找到答案立刻返回 bfs保证是最小的
        if ( ++ step == 10) return -1;//超过10次拓展 返回失败
    }

    return -1;
}

int main()
{
    cin >> A >> B;
    while (cin >> a[n] >> b[n]) n ++ ;

    int t = bfs();
    if (t == -1) puts("NO ANSWER!");
    else cout << t << endl;

    return 0;
}


数据分组

小猫爬山)https://www.acwing.com/problem/content/167/
把每个猫的重量 放入凑成一团 看最多装多少组
每只猫两个状态 新开一个团(最优解可能在新开车里面) / 放在已经开启的团里
每递归一层就满足一次

(枚举的是车)
int sum[N];//sum表示每辆车的和
void dfs(int u, int k)//k示枚举到第几辆的车
{
    // 最优性剪枝
    if (k >= ans) return;
    if (u == n)//u是枚举到第几个数字,因为u除了前进
    {
        ans = k;
        return;
    }

    for (int i = 0; i < k; i ++ )//对于第k辆车之前的车
        if (sum[i] + w[u] <= m) // 可行性剪枝,这里是看看哪个车可以放就放那辆车
        {
            sum[i] += w[u];
            dfs(u + 1, k);
            sum[i] -= w[u]; // 恢复现场
        }

    // 新开一辆车
    sum[k] = w[u];//sum表示当前车的总和 始终用一个sum表示 当前车的长度
    dfs(u + 1, k + 1);
    sum[k] = 0; // 恢复现场
}
 sort(w, w + n);
 reverse(w, w + n);

 dfs(0, 0);


定期重构

迷宫https://ac.nowcoder.com/acm/contest/30825/C
修改点+查询操作
直接for会tle
确定一个阈值 每次查询如果点小于这个阈值按照在里面的点来 当超过了这个阈值 就bfs一边并且清空搞这些点
需要了解什么时候设置这个阈值
如果不知道阈值 怎么算 就直接每次查询的时候bfs一遍 因为起点只有一个 所以有的时候只需要修改少量的数据

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=5e5+5;
ll n,m,h,qw;
struct pp
{
    ll x,y,z,dis;
};
ll change(ll x,ll y,ll z)
{
    return x-1+(y-1)*n+(z-1)*n*m;
}
ll vis[MAXN];
ll dx[10]={1,0,0,-1,0,0};
ll dy[10]={0,1,0,0,-1,0};
ll dz[10]={0,0,1,0,0,-1};
ll ans[MAXN];
ll mnx=1e18,mxx=0,mny=1e18,mxy=0,mnz=1e18,mxz=0;
bool check(ll x,ll y,ll z)
{
    return x >= 1 && x <=n  && y >= 1 && y <= m && z >= 1 && z <= h;
}
queue<pp>q;
void bfs()
{
    while(!q.empty())
    {
        pp ro=q.front();
        q.pop();
        for(int i=0;i<6;++i)
        {
            ll tx=ro.x+dx[i];
            ll ty=ro.y+dy[i];
            ll tz=ro.z+dz[i];
            if(!check(tx,ty,tz))continue;
            if(vis[change(tx,ty,tz)]>ro.dis+1) {
                q.push({tx, ty, tz, ro.dis + 1});
                vis[change(tx,ty,tz)]=ro.dis+1;
            }
        }
    }
}
int main()
{
    ll t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m>>h>>qw;
        for(int i=0;i<=n*m*h;++i)vis[i]=1e18;
        for(int i=0;i<qw;++i)
        {
            ll op,x,y,z;
            scanf("%lld %lld %lld %lld",&op,&x,&y,&z);
            if(op==1)
            {
                q.push({x,y,z,1});
                vis[change(x,y,z)]=1;
            }
            else {
                bfs();
                printf("%lld\n", vis[change(x, y, z)] - 1);
            }
        }
    }
}

https://ac.nowcoder.com/acm/contest/30532/E

bfs 马走日 卡马脚的处理

if(abs(dx[i])==2){
                    if(a[t.x+dx[i]/2][t.y]==1){///||a[t.x+dx[i]][t.y]==1
                        continue;
                    }
                }else{
                    if(a[t.x][t.y+dy[i]/2]==1){//||a[t.x][t.y+dy[i]]==1
                        continue;
                    }
                }
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL pows(LL a, LL x, LL p){if(x==0)return 1; LL t = pows(a, x>>1,p);if(x%2==0)return t*t%p;return t*t%p*a%p;}
int a[310][310];

struct node{int x, y,st=0;};
int vis[310][310];
int n, m;
int k, sx,sy, ex, ey;
int dx[] = {-2,-2,2,2,-1,1,-1,1};
int dy[] = {1,-1,1,-1,-2,-2,2,2};
int bfs(int ok){
    memset(vis,0,sizeof(vis));
    queue<node>q;
    q.push({sx,sy,0});
    while(q.size()){
        node t = q.front();  q.pop();
        if(t.x==ex&&t.y==ey){
            return t.st;
        }
        for(int i = 0; i < 8; i++){
            int nx = t.x+dx[i], ny = t.y+dy[i];
            if(nx<1||nx>n||ny<1||ny>m)continue;
            if(a[nx][ny])continue;
            if(vis[nx][ny])continue;
            if(ok==1){
                if(abs(dx[i])==2){
                    if(a[t.x+dx[i]/2][t.y]==1){///||a[t.x+dx[i]][t.y]==1
                        continue;
                    }
                }else{
                    if(a[t.x][t.y+dy[i]/2]==1){//||a[t.x][t.y+dy[i]]==1
                        continue;
                    }
                }
            }
            q.push({nx,ny,t.st+1});
            vis[nx][ny] = 1;
        }
    }
    return -1;
}

int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;  cin>>T;
    while(T--){
        cin>>n>>m;
        cin>>k>>sx>>sy>>ex>>ey;
        memset(a,0,sizeof(a));
        for(int i = 1; i <= k; i++){
            int tx, ty;  cin>>tx>>ty;
            a[tx][ty] = 1;
        }
        cout<<bfs(0)<<" "<<bfs(1)<<"\n";
    }
    return 0;
}

posted @ 2022-03-07 20:13  liang302  阅读(60)  评论(0编辑  收藏  举报