kuangbin专题一:简单搜索

前:kuangbin的题目比较多,为了简化篇幅,对于一些常规的题目,就不再多写题干了,还望周知。

 

POJ 1321 棋盘问题

思路:dfs

#include <iostream>
using namespace std;
const int maxn = 15;
int n, k;
char mm[maxn][maxn];
bool vis[maxn];

int dfs(int line, int dep){
    if(dep == k+1) return 1;
    int ans = 0;
    for(int i = line; i <= n; i++)
        for(int j = 1; j <= n; j++)
            if(mm[i][j]=='#' and !vis[j]){
                vis[j] = true;
                ans += dfs(i+1, dep+1);
                vis[j] = false;
            }
    return ans;
}

int main(){
    while(~scanf("%d%d", &n, &k) and (n!=-1 and k!=-1)){
        for(int i = 1; i <= n; i++)
            scanf("%s", &mm[i][1]);
        int ans = dfs(1, 1);
        cout << ans << endl;
    }
    return 0;
}
View Code

 

POJ 2251 Dungeon Master

思路:bfs

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 33;
int L, R, C;
char mm[maxn][maxn][maxn];
bool vis[maxn][maxn][maxn];
int st[3], ed[3];
int dx[]={1,-1, 0, 0, 0, 0}, dy[]={0,0,1,-1,0,0}, dz[]={0,0,0,0,1,-1};
struct Pos{
    int x, y, z, dis;
};

int bfs(){
    queue<Pos> q;
    Pos stpos = {st[0], st[1], st[2], 0};
    q.push(stpos);
    vis[st[0]][st[1]][st[2]] = true;

    while(q.size()){
        Pos cur = q.front(); q.pop();
        int ux = cur.x, uy = cur.y, uz = cur.z;
        if(ux==ed[0] and uy==ed[1] and uz==ed[2]) return cur.dis;
        for(int i = 0; i < 6; i++){
            int vx = ux+dx[i], vy = uy+dy[i], vz = uz+dz[i];
            if(vx>0 and vx<=L and vy>0 and vy<=R and vz>0 and vz<=C){
                if(!vis[vx][vy][vz] and mm[vx][vy][vz] != '#'){
                    vis[vx][vy][vz] = true;
                    Pos tmppos = {vx, vy, vz, cur.dis+1};
                    q.push(tmppos);
                }
            }
        }
    }
    return -1;
}

int main(){
    while(~scanf("%d%d%d", &L, &R, &C) and (L!=0 and R!=0 and C!=0)){
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= L; i++)
            for(int j = 1; j <= R; j++)
                for(int k = 1; k <= C; k++){
                    cin >> mm[i][j][k];
                    if(mm[i][j][k] == 'S') {st[0] = i, st[1] = j, st[2] = k;}
                    if(mm[i][j][k] == 'E') {ed[0] = i, ed[1] = j, ed[2] = k;}
                }
        int ans = bfs();
        if(ans == -1)
            cout << "Trapped!" << endl;
        else
            cout << "Escaped in " << ans << " minute(s)." << endl;
    }
    return 0;
}
View Code

 

POJ 3278 Catch That Cow

思路:bfs,可以有特判,但没必要

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 1e5+5;
int N, K;
bool vis[maxn];
int dist[maxn];

int bfs(){
    queue<int> q;
    q.push(N);
    vis[N] = true;
    while(q.size()){
        int cur = q.front(); q.pop();
        if(cur == K) return dist[K];
        if(cur >= 1 and !vis[cur-1]){
            vis[cur-1] = true;
            dist[cur-1] = dist[cur] + 1;
            q.push(cur-1);
        }
        if(cur <= K and !vis[cur+1]){
            vis[cur+1] = true;
            dist[cur+1] = dist[cur] + 1;
            q.push(cur+1);
        }
        if(cur < maxn/2 and !vis[cur<<1]){
            vis[cur<<1] = true;
            dist[cur<<1] = dist[cur] + 1;
            q.push(cur<<1);
        }
    }
    return -1;
}

int main(){
    cin >> N >> K;
    cout << bfs() << endl;
    return 0;
}
View Code

 

POJ3279 Fliptile

题意:有M×N的棋盘,棋盘每个格子为0或者1,每次可以选取一个格子,翻转该格子和其上下左右的四个格子的数字(0变成1,或者1变成0),求让整个棋盘全变成0需要最少的步骤。

思路:首先我们发现,要到达最终结果每个格子最多被翻一次,因为每次翻格子相当于对这个格子及周围格子做了一次异或操作,做两次就等于没有做任何操作。

   也就是说我们要对整个棋盘做搜索,最多有215×15种情况,这显然是会超时的。注意到每行格子的颜色只有上下两行以及本行能够影响,所以可以剪枝去掉多余的情况。具体的,我们遍历完第一行翻转的所有情况,然后对于第一行的每一种情况,我们可以知道第二行哪些格子应该翻转,第三行也参照第二行的情况来决定,依此类推。

#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 16;
const int INF = 0x3f3f3f3f;
int N, M;
int a[maxn][maxn], b[maxn][maxn], c[maxn][maxn];
int dx[]={1,-1,0,0, 0}, dy[]={0,0,1,-1, 0};

void fillLine(int line, int mask){
    for(int i = 1; i <= N; i++)
        b[line][i] = (mask>>(N-i)) & 1;
}

bool isFlippable(int r, int c){
    bool res = a[r][c];
    for(int i = 0; i < 5; i++){
        int nr = r + dx[i], nc = c + dy[i];
        if(nr>=1 and nr<=M and nc>=1 and nc<=N)
            res ^= b[nr][nc];
    }
    return res;
}

int solve(){
    int step = 0;
    for(int i = 2; i <= M; i++)
        for(int j = 1; j <= N; j++)
            if(isFlippable(i-1, j))   // 如果上一行需要本行翻转
                b[i][j] = 1;

    for(int i = 1; i <= N; i++){    // 检查最后一行是否满足条件
        if(isFlippable(M, i))
            return INF;
    }
    for(int i = 1; i <= M; i++)
        for(int j = 1; j <= N; j++)
            step += b[i][j];
    return step;
}

void Print(){
    for(int i = 1; i <= M; i++){
        for(int j = 1; j <= N; j++)
            cout << c[i][j] << ' ';
        cout << endl;
    }
    cout << endl;
}

int main(){
    int result = INF;
    cin >> M >> N;
    for(int i = 1; i <= M; i++)
        for(int j = 1; j <= N; j++)
            cin >> a[i][j];

    for(int i = 0; i < (1<<N); i++){
        memset(b, 0, sizeof(b));
        fillLine(1, i);    // 填充第一行
        int totStep = solve();
        if(totStep < result){
            result = totStep;
            memcpy(c, b, sizeof(b));
        }
    }

    if(result == INF)
        cout << "IMPOSSIBLE" << endl;
    else
        Print();
    return 0;
}
View Code

 

POJ1426 Find The Multiple

题意:给一个正整数n,找到任意一个n的非零倍数m,使得m的所有数字都是0或者1,m的位数不超过100位。

思路:这题比较坑的地方在于它的误导性,结果根本不需要100位,20位就足够了。对不超过20位的m的每个数位进行0,1遍历,如果恰好是n的倍数,那么输出该结果。尝试了使用bfs,发现会tle,但是dfs却ac了。。。

// dfs
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 16;
const int INF = 0x3f3f3f3f;
int n;
bool found;

void dfs(int pos, long long r){
    if(pos >= 20 || found)
        return;
    if(r % n == 0){
        found = true;
        cout << r << endl;
        return;
    }
    dfs(pos + 1, r * 10);
    dfs(pos + 1, r * 10 + 1);
}

int main(){
    while(cin >> n and n){
        found = false;
        dfs(1, 1);
    }
    return 0;
}
View Code

 

POJ3126 Prime Path

思路:bfs。自己写的bfs一直wa,有点崩溃,这里贴上以前不知道在哪抄的代码。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<limits.h>
using namespace std;
const int maxn = 10001;
int a[maxn], vis[maxn];
int d[] = {1, 10, 100, 1000};

int main()
{
    int n;
    cin >> n;
    for(int i = 2; i < maxn; i++)
        if(a[i] == 0)
            for(int j = i*i; j < maxn; j += i)
                a[j] = 1;
    while(n--)
    {
        memset(vis, -1, sizeof(vis));
        int x, y;
        cin >> x >> y;
        queue<int> q;
        q.push(x);
        vis[x] = 0;
        while(!q.empty())
        {
            int tmp = q.front(); q.pop();
            if(tmp == y)
            {
                cout << vis[tmp] << endl;
                continue;
            }
            for(int i = 0; i < 4; i++)
            {
                for(int j = i==3?1:0; j <= 9; j++)
                {
                    int nextn = tmp-((tmp/d[i]) % 10) * d[i] + j * d[i];
                    if(a[nextn]==0 && vis[nextn]==-1)
                    {
                        q.push(nextn);
                        vis[nextn] = vis[tmp] + 1;
                    }
                }
            }
        }
    }
    return 0;
}
View Code

 

POJ3087 Shuffle'm Up

思路:简单遍历,但是提交后编译器一直在字符串比较时报错,(((φ(◎ロ◎;)φ))),还是用以前的代码好了。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
int n, C;
string ed, s1, s2;

int f()
{
    map<string, int> vis;
    string tem = s1 + s2;
    int step = 0;
    while(tem != ed && !vis[tem])
    {
        vis[tem] = 1;
        step++;
        string newS = tem;
        int index = 0;
        for(int i = 0; i < C; i++)
        {
            newS[index++] = tem[i+C];
            newS[index++] = tem[i];
        }
        tem = newS;
    }
    if(tem == ed) return step;
    return -1;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> C;
        cin >> s1 >> s2 >> ed;
        cout << i << " " << f() << endl;
    }
    return 0;
}
View Code

 

POJ3414 Pots

思路:bfs。这题一开始写了很大一串代码,翻了翻以前写的,发现直接将结果写入到一个字符串中更加的方便,给了我很大的启发。LJ编译器,string库问题好大,直接CE。以前代码+1,虽然写的有点蠢,不够优雅,将就着看吧。

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 105;
int A, B, C;
bool vis[maxn][maxn];

struct Pair{
    int a, b, step;
    string op;
    Pair(int aa, int bb, int sstep, string oop = ""){
        a = aa, b = bb, step = sstep;
        op = oop;
    }
};

Pair Fill(Pair &p, string seq){
    int a = seq[0] == '1' ? A : p.a;
    int b = seq[0] == '1' ? p.b : B;
    Pair res{a, b, p.step+1, p.op + "FILL("+seq+")\n"};
    return res;
}

Pair Drop(Pair &p, string seq){
    int a = seq[0] == '1' ? 0 : p.a;
    int b = seq[0] == '1' ? p.b : 0;
    Pair res{a, b, p.step+1, p.op + "DROP("+seq+")\n"};
    return res;
}

Pair Pour(Pair &p, string sq1, string sq2){
    int aa, bb;
    if(sq1[0] == '2'){
        aa = min(A, p.a+p.b);
        bb = p.a+p.b - aa;
    }
    else{
        bb = min(B, p.a+p.b);
        aa = p.a+p.b - bb;
    }
    Pair res{aa, bb, p.step+1, p.op + "POUR("+sq1+","+sq2+")\n"};
    return res;
}

Pair bfs(){
    queue<Pair> q;
    q.push({0, 0, 0});
    vis[0][0] = true;
    while(q.size()){
        Pair p = q.front(); q.pop();
        if(p.a == C or p.b == C) return p;

        if(!vis[A][p.b]){
            vis[A][p.b] = true;
            q.push(Fill(p, "1"));
        }
        if(!vis[p.a][B]){
            vis[p.a][B] = true;
            q.push(Fill(p, "2"));
        }
        if(!vis[0][p.b]){
            vis[0][p.b] = true;
            q.push(Drop(p, "1"));
        }
        if(!vis[p.a][0]){
            vis[p.a][0] = true;
            q.push(Drop(p, "2"));
        }
        if(p.b > 0){
            Pair np = Pour(p, "2", "1");
            if(!vis[np.a][np.b]){
                vis[np.a][np.b] = true;
                q.push(np);
            }
        }
        if(p.a > 0){
            Pair np = Pour(p, "1", "2");
            if(!vis[np.a][np.b]){
                vis[np.a][np.b] = true;
                q.push(np);
            }
        }
    }
    Pair res{-1, -1, -1};
    return res;
}

int main(){
    cin >> A >> B >> C;
    Pair res = bfs();
    if(res.step == -1){
        cout << "impossible" << endl;
        return 0;
    }
    cout << res.step << endl;
    cout << res.op;
    return 0;
}
View Code

 

FZU2150 Fire Game

思路:终于遇到不是POJ的题了。如果要分情况讨论就很麻烦,看到数据规模较小(地图长宽不超过10),我们可以直接遍历所有可能的起始点组合。这题给我的启发是能交给机器做的事,就不要交给人去做。

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 12;
const int INF = 0x3f3f3f3f;
int T, n, m;
char mm[maxn][maxn];
bool vis[maxn][maxn];
struct Pos{
    int x, y, step;
};
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};

inline bool is_valide(int x, int y){
    bool res = x>=1 && x<=n && y>=1 && y<=m;
    res = res && mm[x][y]=='#' && !vis[x][y];
    return res;
}

int bfs(int x1, int y1, int x2, int y2){
    int res = 0;
    queue<Pos> q;
    q.push((Pos){x1, y1, 0});
    q.push((Pos){x2, y2, 0});
    vis[x1][y1] = vis[x2][y2] = true;
    while(q.size()){
        Pos p = q.front(); q.pop();
        for(int i = 0; i < 4; i++){
            int nx = p.x + dx[i], ny = p.y + dy[i];
            if(is_valide(nx, ny)){
                vis[nx][ny] = true;
                q.push((Pos){nx, ny, p.step+1});
                res = max(res, p.step+1);
            }
        }
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            if(mm[i][j]=='#' and !vis[i][j])
                return INF;
    return res;
}

int main(){
    cin >> T;
    for(int t = 1; t <= T; t++){
        cin >> n >> m;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                cin >> mm[i][j];

        int res = INF;
        for(int i = 1;  i <= n; i++)
            for(int j = 1; j <= m; j++)
                for(int ii = 1; ii <= n; ii++)
                    for(int jj = 1; jj <= m; jj++){
                        if(mm[i][j]=='#' and mm[ii][jj]=='#'){
                            memset(vis, false, sizeof(vis));
                            res = min(res, bfs(i, j, ii, jj));
                        }
                    }
        cout << "Case " << t << ": ";
        if(res == INF) cout << -1 << endl;
        else cout << res << endl;
    }
    return 0;
}
View Code

 

UVA11624 Fire!

思路:bfs。可以让火和人每次分别走一步,也可以先求出火到达所有点的距离,再求人是否可以跑出去。这题的坑在于火可能不止一个。

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 1002;
const int INF = 0x3f3f3f3f;
int t, r, c;
char mm[maxn][maxn];
bool vis[maxn][maxn];
struct Pos{
    int x, y, step;
    bool j; // is joe here?
};
int dx[]={1,-1,0,0}, dy[]={0,0,1,-1};

inline bool isValide(int x, int y){
    bool res = x>=1 && x<=r && y>=1 && y<=c;
    res = res && mm[x][y]=='.' && !vis[x][y];
    return res;
}

inline bool onBound(int x, int y){
    return x==1 || x==r || y==1 || y==c;
}

queue<Pos> q;
int bfs(){
    while(q.size()){
        Pos p = q.front(); q.pop();
        if(onBound(p.x, p.y) and p.j){
            return p.step;
        }
        for(int i = 0; i < 4; i++){
            int nx = p.x + dx[i], ny = p.y + dy[i];
            if(isValide(nx, ny)){
                vis[nx][ny] = true;
                q.push({nx, ny, p.step+1, p.j});
            }
        }
    }
    return INF;
}

int main(){
    cin >> t;
    while(t--){
        memset(vis, false, sizeof(vis));
        while(q.size()) q.pop();
        cin >> r >> c;
        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++)
                cin >> mm[i][j];

        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++)
                if(mm[i][j] == 'F'){
                    Pos fire = {i, j, 0, false};
                    q.push(fire);
                    vis[i][j] = true;
                }
        for(int i = 1; i <= r; i++)
            for(int j = 1; j <= c; j++)
                if(mm[i][j] == 'J'){
                    Pos joe = {i, j, 0, true};
                    q.push(joe);
                    vis[i][j] = false;
                }
        int res = bfs();
        if(res == INF) cout << "IMPOSSIBLE" << endl;
        else cout << res+1 << endl;
    }
    return 0;
}
View Code

 

POJ 3984 迷宫问题

思路:bfs。POJ的题。。。。字符串运算还是有问题,直接贴上没测试的代码。

#include <iostream>
#include <cstring>
#include <queue>
#include <sstream>
using namespace std;
const int maxn = 10;
const int INF = 0x3f3f3f3f;
char mm[maxn][maxn];
bool vis[maxn][maxn];
int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
struct Pos{
    int x, y;
    string path;
};

inline bool isValide(int x, int y){
    return x>=0 and x<5 and y>=0 and y<5 and mm[x][y]=='0' and !vis[x][y];
}

string bfs(){
    queue<Pos> q;
    q.push({0, 0, "(0, 0)\n"});
    vis[0][0] = true;
    while(q.size()){
        Pos p = q.front(); q.pop();
        if(p.x==4 and p.y==4) return p.path;
        for(int i = 0; i < 4; i++){
            int nx = p.x + dx[i], ny = p.y + dy[i];
            if(isValide(nx, ny)){
                vis[nx][ny] = true;
                q.push({nx, ny, p.path + "(" + to_string(nx) + ", " + to_string(ny) + ")\n"});
            }
        }
    }
}

int main(){
    for(int i = 0; i < 5; i++)
        for(int j = 0; j < 5; j++)
            cin >> mm[i][j];
    cout << bfs() << endl;
    return 0;
}
View Code

 

HDU1241 Oil Deposits

思路:简单bfs或者dfs。

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 105;
const int INF = 0x3f3f3f3f;
int n, m;
char mm[maxn][maxn];
int group[maxn][maxn];
int gid;
struct Pos{
    int x, y;
};

void init(){
    memset(group, 0, sizeof(group));
    gid = 0;
}

void readData(){
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            cin >> mm[i][j];
}

inline bool isValide(int x, int y){
    return x>=1 and x<=n and y>=1 and y<=m and !group[x][y] and mm[x][y]=='@';
}

void bfs(int x, int y){
    gid++;
    queue<Pos> q;
    q.push({x, y});
    group[x][y] = gid;
    while(q.size()){
        Pos p = q.front(); q.pop();
        for(int i = -1; i <= 1; i++)
            for(int j = -1; j <= 1; j++){
                int nx = p.x + i, ny = p.y + j;
                if(isValide(nx, ny)){
                    q.push({nx, ny});
                    group[nx][ny] = gid;
                }
            }
    }
}

int main(){
    while(cin >> n >> m and n and m){
        init();
        readData();
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                if(!group[i][j] and mm[i][j]=='@')
                    bfs(i, j);
        cout << gid << endl;
    }
    return 0;
}
View Code

 

HDU1495 非常可乐

思路:bfs。和上面倒水那题差不多。 评论区看见了一种方法挺有意思的:先算出三杯水体积的公因数,然后三杯水的体积除以这个公因数。如果第一杯水为奇数就不可分,否则就可分,且倒水次数为数字减一。

证明:首先每杯水肯定是他们的公因子的倍数d,把d毫升水看作一个单位,那么三杯水可以陈放S/d,N/d,M/d单位的水,且N/d,M/d互质。显然,当S/d为奇数时无解;为偶数时,先考虑有一杯水的单位为1的情况,那么显然,S/d-1即为答案,倒一次水相当于倒多个单位水的情况,答案也为S/d-1。更严谨的证明以后有时间再写吧。

#include <iostream>
using namespace std;
int s, n, m;

int gcd(int a, int b){
    return a == 0 ? b : gcd(b%a, a);
}

int main(){
    while(cin >> s >> n >> m and s){
        int divisor = gcd(s, n);
        divisor = gcd(divisor, m);
        s /= divisor;
        if(s & 1) cout << "NO" << endl;
        else cout << s-1 << endl;
    }
    return 0;
}
View Code

 

HDU2612 Find a way

思路:bfs。

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 205;
const int INF = 0x3f3f3f3f;
int n, m;
char mm[maxn][maxn];
int dist1[maxn][maxn], dist2[maxn][maxn];
struct Pos{
    int x, y;
};
int dx[]={1,-1,0,0}, dy[]={0,0,1,-1};

inline bool isValide(int x, int y, int (*dist)[maxn]){
    return x>=1 and x<=n and y>=1 and y<=m and mm[x][y]!='#' and dist[x][y]==-1;
}

void bfs(int (*dist)[maxn], Pos st){
    queue<Pos> q;
    q.push(st);
    dist[st.x][st.y] = 0;
    while(q.size()){
        Pos p = q.front(); q.pop();
        for(int i = 0; i < 4; i++){
            int nx = p.x + dx[i], ny = p.y + dy[i];
            if(isValide(nx, ny, dist)){
                dist[nx][ny] = dist[p.x][p.y] + 1;
                q.push({nx, ny});
            }
        }
    }
}

int main(){
    while(cin >> n >> m){
        memset(dist1, -1, sizeof(dist1));
        memset(dist2, -1, sizeof(dist2));
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                cin >> mm[i][j];
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                if(mm[i][j]=='Y') bfs(dist1, (Pos){i, j});
                else if(mm[i][j]=='M') bfs(dist2, (Pos){i, j});

        int res = INF;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                if(mm[i][j]=='@' and dist1[i][j]!=-1 and dist2[i][j]!=-1)
                    res = min(res, dist1[i][j] + dist2[i][j]);
        cout << res * 11 << endl;
    }
    return 0;
}
View Code

 

  现在kuangbin简单搜索专题就结束了,撒花完结。

 

posted @ 2020-10-26 11:15  Nanachi  阅读(168)  评论(0)    收藏  举报