ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

搜索

2022-05-23 23:31:44  阅读:114  来源: 互联网

标签:Map frac int ++ vector 搜索 &&


迷宫(P1605)

题目描述:

给定一个\(N*M\)方格的迷宫,迷宫里有T处障碍,障碍处不可通过。给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案。在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。

int dir[4][2]={
    {1,0},
    {0,1},
    {-1,0},
    {0,-1}
};
int N,M,T;
int sx,sy,fx,fy;
int cnt;
bool Map[6][6];
void dfs(int x,int y){
    if(x<1||x>N)return;
    if(y<1||y>M)return;
    if(!Map[x][y])return;
    if(x==fx&&y==fy){cnt++;return;}
    for(int i=0;i<4;i++){
        Map[x][y]=0;
        dfs(x+dir[i][0],y+dir[i][1]);
        Map[x][y]=1;
    }
}
int main(){
    cin>>N>>M>>T;
    memset(Map,1,sizeof(Map));
    cin>>sx>>sy>>fx>>fy;
    for(int i=0;i<T;i++){
        int tmpx,tmpy;
        cin>>tmpx>>tmpy;
        Map[tmpx][tmpy]=0;
    }
    cnt=0;
    dfs(sx,sy);
    cout<<cnt;
    
    return 0;
}

八数码问题

一、BFS+Cantor展开

二、A* 做法

三、IDA* 做法

N皇后

一、朴素DFS

int ans[15];
int cnt=0;
int n;
bool check(int x,int y){
    for(int i=0;i<x;i++){
        if(ans[i]==y)return 0;
        if(abs(x-i)==abs(y-ans[i]))return 0;
    }
    return 1;
}
void dfs(int t){
    if(t==n){
        if(cnt<3){
            for(int i=0;i<n;i++)cout<<ans[i]+1<<" ";
            cout<<endl;
        }
        cnt++;
        return;
    }
    for(int i=0;i<n;i++){
        if(check(t,i)){
            ans[t]=i;
            dfs(t+1);
        }
    }
}
int main(){
    scanf("%d",&n);
    dfs(0);
    printf("%d",cnt);
    
    return 0;
}

二、二进制优化

位运算太神奇啦!

int mark[1 << 20], put[100];
int n,ans,p;
int lowbit(int x) {   //返回只保留最低位1的数
    return x & (-x);
}
void dfs(int x, int a, int b, int c) {  //a,b,c三个数分别存储列、左对角线、右对角线不能放置棋子的位置
    if (x > n) {
        ans++;
        return;
    }
    int t = (a | b | c) ^ p;   //利用按位或的性质:只要有一个是1结果就是1
                               //如果都是1即不能放的话,与p异或的结果为0,不能再继续搜索
    while (t) {
        int i = lowbit(t);
        put[x] = mark[t];
        dfs(x + 1, a | i, (b | i) >> 1, ((c | i) << 1) & p);
        t -= i;
    }
}
int main() {
    cin >> n;
    p = (1 << n)-1;   //棋盘边界
    mark[0] = 1;
    for (int i = 1; i <= n; i++)mark[1 << i] = i;
    dfs(1, 0, 0, 0);
    cout << ans;

    return 0;
}

网格照明 leetcode1001

  在大小为\(n x n\)的网格\(grid\)上,每个单元格都有一盏灯,最初灯都处于关闭 状态。
  给你一个由灯的位置组成的二维数组 lamps ,其中\(lamps[i] = [row_{i}, col_{i}]\)表示 打开 位于\(grid[row_{i}][col_{i}]\)的灯。即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态。
当一盏灯处于打开状态,它将会照亮 自身所在单元格 以及同一 行 、同一 列 和两条 对角线 上的 所有其他单元格 。
  另给你一个二维数组queries,其中\(queries[j] = [row_{j}, col_{j}]\)。对于第\(j\) 个查询,如果单元格\([row_{j}, col_{j}]\)是被照亮的,则查询结果为\(1\),否则为\(0\)。在第 j 次查询之后[按照查询的顺序] ,关闭位于单元格\(grid[row_{j}][col_{j}]\)上及相邻 8 个方向上(与单元格\(grid[row_{j}][col_{j}]\)共享角或边的任何灯。
  返回一个整数数组\(ans\)作为答案,\(ans[j]\)应等于第\(j\)次查询\(queries[j]\)的结果,1 表示照亮,0 表示未照亮。

解析

  • 本题重点是对两个对角线灯的处理,可以用\(45^{\circ}\)的斜线所对应的截距表示

代码

class Solution {
public:
    vector<int> gridIllumination(int n, vector<vector<int>>& lamps, vector<vector<int>>& queries) {
        unordered_map<int,int> row,col,xie,rev_xie;
        set<pair<int,int>> lights;
        int len=lamps.size();
        for(int i=0;i<len;i++){
            int x=lamps[i][0],y=lamps[i][1];
            if(lights.find({x,y})==lights.end()){
                lights.insert({x,y});
                row[x]++;
                col[y]++;
                xie[x-y]++;
                rev_xie[x+y]++;
            }
        }
        len=queries.size();
        vector<int> ans (len,0);
        for(int i=0;i<len;i++){
            int x=queries[i][0],y=queries[i][1];
            if(row[x]||col[y]||xie[x-y]||rev_xie[x+y])ans[i]=1;
            for(int j=0;j<9;j++){
                int nx=x+dir[j][0];
                int ny=y+dir[j][1];
                if(nx>=0&&nx<n&&ny>=0&&ny<n){
                    if(lights.find({nx,ny})!=lights.end()){
                        lights.erase({nx,ny});
                        row[nx]--;
                        col[ny]--;
                        xie[nx-ny]--;
                        rev_xie[nx+ny]--;
                    }
                }
            }
        }
        return ans;
    }
private:
    int dir[9][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{1,1},{-1,1},{1,-1},{0,0}};
};

打开所有的灯 P2040

解法一 DFS (略)

解法二 异或方程组 (坑,待填)

#include<iostream>
using namespace std;
bool a[101][101],b[101][101],ans[101];  //友情提示:一定要用bool!!!
int fx[5]={0,-1,0,1,0};
int fy[5]={0,0,1,0,-1};   //方向数组
int main(){
    for(int i=1;i<=3;i++){
        for(int j=1;j<=3;j++){
            cin>>a[i][j];
            a[i][j]=!a[i][j];
        }
    }       //输入
    int t=1;
    for(int i=1;i<=3;i++){
        for(int j=1;j<=3;j++){
            for(int k=1;k<=4;k++){
                int x=i+fx[k];
                int y=j+fy[k];
                if(x>=1&&x<=3&&y>=1&&y<=3){
                    b[t][x*3-3+y]=a[x][y];
                }
            }
            b[t][i*3-3+j]=a[i][j];
            b[t][10]=a[i][j];
            t++;
        }
    }   //将这九盏灯的情况转化为异或方程组
    for(int k=1;k<=8;k++){
        int p=k;
        for(int i=k;i<=9;i++){
            if(b[i][k]>b[p][k])p=i;
        }
        for(int i=1;i<=10;i++){
            swap(b[k][i],b[p][i]);
        }
        for(int i=k+1;i<=9;i++){
            if(!b[i][k])continue;  //要注意分母为0的情况
            for(int j=k;j<=10;j++){
                b[i][j]=b[k][j]^b[i][j];
            }
        }
    }     //解异或方程组
    for(int i=9;i>=1;i--){
        if(!b[i][i])continue;  //要注意分母为0的情况
        int s=0;
        for(int j=i+1;j<=9;j++){
            if(b[i][j])s+=ans[j];
        }
        ans[i]=b[i][10]-s;
    }    //求解
    int sum=0;
    for(int i=1;i<=9;i++){
        if(ans[i])sum++;
    }        //解中有几个为1答案就为几
    cout<<sum<<endl;   //输出
    return 0;
}

马的遍历 P1443

本题解析

要用队列而不是递归(即不能用DFS),普通DFS没有搜索的层次感,会在一路搜索中串到另一路上去。

代码实现

static constexpr int dir[8][2] = { {1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1} };
int n, m, x, y;
void bfs(int step, vector<vector<int>>& Map, queue<pair<int, int>>& q) {
    while (!q.empty()) {
        int xx = q.front().first, yy = q.front().second;
        q.pop();
        for (int i = 0; i < 8; i++) {
            int nx = xx + dir[i][0];
            int ny = yy + dir[i][1];
            if (nx >= 0 && ny >= 0 && nx < n && ny < m && Map[nx][ny] == 0) {
                q.push({ nx,ny });
                Map[nx][ny] = Map[xx][yy] + 1;
            }
        }
    }
}
int main() {
    cin >> n >> m >> x >> y;
    vector<vector<int>> Map(n, vector<int>(m, 0));
    queue<pair<int, int>> q;
    q.push({ x - 1,y - 1 });
    Map[x - 1][y - 1] = 1;
    bfs(1, Map, q);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            printf("%-5d", Map[i][j] - 1);
        }
        cout << endl;
    }

    return 0;
}

Meteor Shower S P2895 (大坑题)

  • 坑点:牛可以走出地图、陨石可以砸出地图
  • 意思是牛只要能走到一个不会被陨石砸的地方就行了

代码

vector<vector<int>> Map(305, vector<int>(305, INT_MAX));
vector<vector<int>> trip(305, vector<int>(305, 0));
static constexpr int dir[5][2] = { {-1,0},{1,0},{0,1},{0,-1},{0,0} };
int m;
int main() {
    cin >> m;
    while (m--) {
        int x, y, t;
        cin >> x >> y >> t;
        for (int i = 0; i < 5; i++) {
            int nx = x + dir[i][0];
            int ny = y + dir[i][1];
            if (nx >= 0 && ny >= 0 && Map[nx][ny]>t) {
                Map[nx][ny] = t;   //用时间标记陨石坑
            }
        }
    }
    queue<int> q;
    q.emplace(0);
    while (!q.empty()) {
        int tmp = q.front();
        q.pop();
        int x = tmp / 305, y = tmp % 305;
        if (Map[x][y] == INT_MAX ) { cout << trip[x][y]; return 0; }  //如果找到安全的地方就输出
        for (int i = 0; i < 4; i++) {
            int nx = x + dir[i][0], ny = y + dir[i][1];
            if (nx == 0 && ny == 0)continue;  //由于我用trip同时存走过的地方和时间导致(0,0)处是0没法判断
            if (nx >= 0 && ny >= 0 && nx < 305 && ny < 305 && trip[nx][ny] == 0 && trip[x][y] + 1 < Map[nx][ny]) {
                trip[nx][ny] = trip[x][y] + 1;
                q.emplace(nx * 305 + ny);
            }
        }
    }
    cout << -1;

    return 0;
}

IDA

一、简介

  迭代加深是一种 每次限制搜索深度的 深度优先搜索。
  它的本质还是深度优先搜索,只不过在搜索的同时带上了一个深度 ,当达到设定的深度时就返回,一般用于找最优解。如果一次搜索没有找到合法的解,就让设定的深度加一,重新从根开始。
  既然是为了找最优解,为什么不用 BFS 呢?我们知道 BFS 的基础是一个队列,队列的空间复杂度很大,当状态比较多或者单个状态比较大时,使用队列的 BFS 就显出了劣势。事实上,迭代加深就类似于用 DFS 方式实现的 BFS,它的空间复杂度相对较小。
  当搜索树的分支比较多时,每增加一层的搜索复杂度会出现指数级爆炸式增长,这时前面重复进行的部分所带来的复杂度几乎可以忽略,这也就是为什么迭代加深是可以近似看成 BFS 的。

二、例题 P1763 埃及分数

题目描述

在古埃及,人们使用单位分数的和(形如 \(\dfrac{1}{a}\) 的,\(a\) 是自然数)表示一切有理数。如:\(\dfrac{2}{3} = \dfrac{1}{2} + \dfrac{1}{6}\),但不允许 \(\dfrac{2}{3} = \dfrac{1}{3} + \dfrac{1}{3}\),因为加数中有相同的。对于一个分数 \(\dfrac{a}{b}\),表示方法有很多种,但是哪种最好呢?首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。如:

\[\begin{aligned} \frac{19}{45} &= \frac{1}{3} + \frac{1}{12} + \frac{1}{180}\\ \frac{19}{45} &= \frac{1}{3} + \frac{1}{15} + \frac{1}{45}\\ \frac{19}{45} &= \frac{1}{3} + \frac{1}{18} + \frac{1}{30}\\ \frac{19}{45} &= \frac{1}{4} + \frac{1}{6} + \frac{1}{180}\\ \frac{19}{45} &= \frac{1}{5} + \frac{1}{6} + \frac{1}{18}\\ \end{aligned} \]

最好的是最后一种,因为 \(\dfrac{1}{18}\) 比 \(\dfrac{1}{180}, \dfrac{1}{45}, \dfrac{1}{30}\) 都大。
注意,可能有多个最优解。如:

\[\begin{aligned} \frac{59}{211} &= \frac{1}{4} + \frac{1}{36} + \frac{1}{633} + \frac{1}{3798}\\ \frac{59}{211} &= \frac{1}{6} + \frac{1}{9} + \frac{1}{633} + \frac{1}{3798}\\ \end{aligned} \]

由于方法一与方法二中,最小的分数相同,因此二者均是最优解。

给出 \(a,b\),编程计算最好的表达方式。保证最优解满足:最小的分数 \(\ge \cfrac{1}{10^7}\)。

输入格式

一行两个整数,分别为 \(a\) 和 \(b\) 的值。

输出格式

输出若干个数,自小到大排列,依次是单位分数的分母。

思路解析

代码实现

#define int long long
#define MAXN 1010
int a,b,depth;
int ans[MAXN],tmp[MAXN];
bool flag;
int gcd(int x,int y){
    return (!y?x:gcd(y,x%y));
}
void dfs(int newa,int newb,int step){
    if(step+1==depth){
        if(newa==1){
            tmp[step+1]=newb;
            flag=1;
            if(!ans[depth]||tmp[depth]<ans[depth]){
                memcpy(ans,tmp,sizeof(tmp));
            }
        }
        return;
    }
    
    int first=newb%newa?newb/newa+1:newb/newa;
    for(int i=max(tmp[step]+1,first);i<=ceil(newb/newa)*(depth-step);++i){
        tmp[step+1]=i;
        int na=newa*i-newb;
        int nb=newb*i;
        int g=gcd(na,nb);
        dfs(na/g,nb/g,step+1);
    }
}
signed main(){
    cin>>a>>b;
    int g=gcd(a,b);
    tmp[0]=1;
    
    for(depth=1;;depth++){
        dfs(a/g,b/g,0);
        if(flag)break;
    }
    for(int i=1;i<=depth;i++){
        printf("%d ",ans[i]);
    }
    
    return 0;
}

统计最高分数结点——leetcode

  1. T掉的方法——暴力DFS
class Solution {
public:
    int dfs(int start,vector<vector<int>>& link,vector<bool>& used){
        if(link[start].size()==1)return 1;
        int sum=0;
        for(auto i:link[start]){
            if(used[i]==0){used[i]=1;sum+=dfs(i,link,used);}
        }
        return sum+1;
    }
    int countHighestScoreNodes(vector<int>& parents) {
        int len=parents.size();
        int mx=0,ans=0;
        vector<vector<int>> link(len);
        for(int i=0;i<len;i++){
            if(parents[i]!=-1)link[i].push_back(parents[i]);
            if(parents[i]!=-1)link[parents[i]].push_back(i);
        }
        for(int i=0;i<len;i++){
            int tmp=1;
            for(auto j:link[i]){
                vector<bool> used(len,0);
                used[i]=1,used[j]=1;
                tmp*=dfs(j,link,used);
            }
            if(tmp>mx){mx=tmp,ans=1;}
            else if(tmp==mx)ans++;
        }
        return ans;
    }
};
  1. 正解—— 还是DFS,但是是记忆化搜索
class Solution {
public:
    long max_score=0;
    int len,ans=0;
    vector<vector<int>> children;
    int dfs(int root){
        long score=1;
        int size=len-1;
        for(auto i:children[root]){
            int tmp=dfs(i);  //找出以该节点为根的树的大小,同时更新该节点的分数
            size-=tmp;
            score*=tmp;
        }
        if(root!=0)score*=size;
        if(score>max_score){max_score=score;ans=1;}
        else if(score==max_score)ans++;
        return len-size;
    }
    int countHighestScoreNodes(vector<int>& parents) {
        this->len=parents.size();
        this->children=vector<vector<int>>(len);
        for(int i=0;i<len;i++){
            if(parents[i]!=-1)children[parents[i]].emplace_back(i);
        }
        dfs(0);
        return ans;
    }
};

草稿

class Solution {
    typedef pair<int, int> pii;
    int n, m;
    vector<vector<int>> MAP;
    vector<vector<long long>> fire;

    short dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};

    void bfs() {
        queue<pii> q;
        fire.resize(n, vector<long long>(m));
        for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) fire[i][j] = 1e18;
        for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (MAP[i][j] == 1)
            q.push(pii(i, j)), fire[i][j] = 0;
        while (!q.empty()) {
            pii p = q.front(); q.pop();
            int i = p.first, j = p.second;
            for (int k = 0; k < 4; k++) {
                int ii = i + dir[k][0], jj = j + dir[k][1];
                if (ii < 0 || jj < 0 || ii >= n || jj >= m || fire[ii][jj] < 1e18 || MAP[ii][jj]) continue;
                fire[ii][jj] = fire[i][j] + 1;
                q.push(pii(ii, jj));
            }
        }
    }

    bool check(int LIM) {
        queue<pii> q;
        vector<vector<long long>> dis;
        dis.resize(n, vector<long long>(m));
        for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) dis[i][j] = 1e18;
        q.push(pii(0, 0)); dis[0][0] = LIM;
        while (!q.empty()) {
            pii p = q.front(); q.pop();
            int i = p.first, j = p.second;
            for (int k = 0; k < 4; k++) {
                int ii = i + dir[k][0], jj = j + dir[k][1];
                if (ii < 0 || jj < 0 || ii >= n || jj >= m || dis[ii][jj] < 1e18 || MAP[ii][jj]) continue;
                dis[ii][jj] = dis[i][j] + 1;
                if (ii == n - 1 && jj == m - 1) return dis[ii][jj] <= fire[ii][jj];
                else if (dis[ii][jj] < fire[ii][jj]) q.push(pii(ii, jj));
            }
        }
        return false;
    }

public:
    int maximumMinutes(vector<vector<int>>& grid) {
        n = grid.size(); m = grid[0].size();
        MAP = grid;
        bfs();
        if (!check(0)) return -1;

        int head = 0, tail = 1e9;
        while (head < tail) {
            int mid = (head + tail + 1) >> 1;
            if (check(mid)) head = mid;
            else tail = mid - 1;
        }
        return head;
    }
};

标签:Map,frac,int,++,vector,搜索,&&
来源: https://www.cnblogs.com/wasser007/p/16303875.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有