zoukankan      html  css  js  c++  java
  • 高级搜索题集

    题集转自以下链接:

    https://blog.csdn.net/shahdza/article/details/7986044


     胜利大逃亡(续) HDU - 1429 

    题意:就是二维地图,有障碍;重点有门,和钥匙(用于开门)。

    理解:因为找钥匙,这个解答路径可能会走回头路。

    思路:第一遍,因为有时间限制,并且20*20;所以简单bfs,然后MLimit了。感觉就是重复存储点在队列。

    那么难点就在于判重了。因为走回头路,简单二维判重肯定不行。

    回想和之前炮台那题,因为子弹的原因也是走回头路,所以判重数组多加了一个时间维度。

    这题关键因素是钥匙,所以加一个钥匙拥有状态的判重,就ac了。(用2进制状态压缩,这里因为a-j,所以数组开个1024就够了。)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    
    using namespace std;
    
    int n,m,t;
    char ditu[21][21];
    bool vis[1025][21][21];
    int Sx,Sy,Fx,Fy;
    
    int xx[] = {0,0,1,-1};
    int yy[] = {1,-1,0,0};
    
    struct node{
        int x,y,step,k=0;
        node(int a,int b,int c,int d):x(a),y(b),step(c),k(d) {};
    };
    
    
    bool bfs(){
        memset(vis,false,sizeof vis);
        queue<node> q;q.push(node(Sx,Sy,0,0));vis[0][Sx][Sy] = true;
        while(!q.empty()){
            node p = q.front();q.pop();
            for(int i=0;i<4;i++){
                int x = p.x+xx[i],y = p.y+yy[i];
                if(x<0||x>=n||y<0||y>=m||ditu[x][y]=='*'||p.step+1>=t) continue;
                int step = p.step + 1,k = p.k;
                if(ditu[x][y]>='A'&&ditu[x][y]<='Z'&&!(p.k&(1<<ditu[x][y]-'A'))) continue;
                if(ditu[x][y] == '^') {cout<<step<<endl;return true;}
                if(ditu[x][y]>='a'&&ditu[x][y]<='z') k |= 1<<(ditu[x][y]-'a');
                if(vis[k][x][y]) continue;
                q.push(node(x,y,step,k));vis[k][x][y] = true;
            }
        }
        return false;
    }
    
    int main()
    {
        while(cin>>n>>m>>t){
            for(int i=0;i<n;i++)for(int j=0;j<m;j++){
                char ch = getchar();while(ch==' '||ch=='
    ') ch = getchar();
                ditu[i][j] = ch;
                if(ch=='^') Fx = i,Fy = j;
                else if(ch=='@') Sx = i,Sy = j;
            }
    
            if(!bfs()) puts("-1");
        }
        return 0;
    }
    View Code

     这题给我启发就是,判重作用在于剪枝,避免重复的入队操作。那么关键在于什么?关键在于点状态更新,并且取决于什么要素关键,炮台那题,因为子弹原因,时间是关键。这题因为门的原因,钥匙是关键。所以添加关键要素的判重数组是解题关键。



    Key Task HDU - 1885 

    题意与上题一样。但是地图大小和钥匙,门出口数量不同。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    int n,m;
    char ditu[101][101];
    bool vis[17][101][101];
    int Sx,Sy,Fx,Fy;
    
    int xx[] = {0,0,1,-1};
    int yy[] = {1,-1,0,0};
    
    struct node{
        int x,y,step,k=0;
        node(int a,int b,int c,int d):x(a),y(b),step(c),k(d) {};
    };
    
    int mp[5];
    
    bool bfs(){
        memset(vis,false,sizeof vis);
        queue<node> q;q.push(node(Sx,Sy,0,0));vis[0][Sx][Sy] = true;
        while(!q.empty()){
            node p = q.front();q.pop();
            for(int i=0;i<4;i++){
                int x = p.x+xx[i],y = p.y+yy[i];
                if(x<0||x>=n||y<0||y>=m||ditu[x][y]=='#') continue;
                int step = p.step + 1,k = p.k;
                if(ditu[x][y] == 'X') {cout<<"Escape possible in "<<step<<" steps."<<endl;return true;}
                if(ditu[x][y]>='A'&&ditu[x][y]<='Z'&&!(p.k&(1<<mp[ditu[x][y]-'A']))) continue;
                if(ditu[x][y]>='a'&&ditu[x][y]<='z') k |= 1<<mp[ditu[x][y]-'a'];
                if(vis[k][x][y]) continue;
                q.push(node(x,y,step,k));vis[k][x][y] = true;
            }
        }
        return false;
    }
    
    int main()
    {
        mp['B'-'A'] = 0,mp['Y'-'A'] = 1,mp['R'-'A'] = 2,mp['G'-'A'] = 3;
        while(cin>>n>>m&&n&&m){
            for(int i=0;i<n;i++)for(int j=0;j<m;j++){
                char ch = getchar();while(ch==' '||ch=='
    ') ch = getchar();
                ditu[i][j] = ch;
                if(ch=='X') Fx = i,Fy = j;
                else if(ch=='*') Sx = i,Sy = j;
            }
    
            if(!bfs()) puts("The poor student is trapped!");
        }
        return 0;
    }
    View Code

    扩展思考:我这里想到如果钥匙并非永久产品而是一次性,那该怎么办?

    我感觉难点可能在于不止走一次回头路。

    我一开始能想到就是:像多重背包操作一样,将问题转化为0-1,多个相同门和钥匙,对应将他们分类成不同颜色。额,好像不行,因为你不清楚那个钥匙对应哪个门即为最优。。。。

    那判重函数多加一维度,有什么颜色钥匙,并且多一个钥匙数量的维度。


     超级密码HDU - 1226 

    算法:BFS

    思路:BFS向下操作,*进制数+构成数 然后 对N求余 得到 的余数,因为是要正整数倍,所以当余数为0则为答案。

    K1%N = K2%N;(K1*C+M)%N = (K2*C+M)%N ;所以余数可以作为判重点。相同余数不需要重复入队。

    用数组记录答案长度,因为BFS是有层次性,所以超过500就可以判负了。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    int N,C,M;
    int yS[17],ySlen;
    int vis[5005],pre[5005],ans[5005],cnt[5005];
    
    void init(){
        memset(vis,0,sizeof vis);
        memset(cnt,0,sizeof cnt);
        memset(pre,-1,sizeof pre);
        scanf("%d%d%d",&N,&C,&M);
        for(int i=0;i<M;i++){
            char ch = getchar();while(ch == ' '|| ch == '
    ') ch = getchar();
            if(ch>='0'&&ch<='9') yS[i] = ch - '0';
            else yS[i] = ch - 'A' + 10;
        }
        sort(yS,yS+M);
        ySlen = unique(yS,yS+M) - yS;
    }
    
    void showAns(int num){
        if(num == -1) return ;
        showAns(pre[num]);
        if(ans[num]<10) printf("%d",ans[num]);
        else printf("%c",ans[num] - 10 + 'A');
    }
    
    bool solve(){
        queue<int> q;
        for(int i=0;i<ySlen;i++){
            if(!yS[i]) continue;
            int tnt = yS[i] % N;
            if(vis[tnt]) continue;
            vis[tnt] = 1;cnt[tnt] = 1;
            ans[tnt] = yS[i];
            if(!tnt) return true;
            q.push(tnt);
        }
        while(!q.empty()){
            int now = q.front();q.pop();
            if(cnt[now]>500) return false;
            for(int i=0;i<ySlen;i++){
                int tnt  = (now*C + yS[i]) % N;
                if(vis[tnt]) continue;
                vis[tnt] = 1;cnt[tnt] = cnt[now] + 1;
                ans[tnt] = yS[i];pre[tnt] = now;
                if(!tnt) return true;
                q.push(tnt);
            }
        }
        return false;
    }
    
    int main()
    {
        int _;scanf("%d",&_);
        while(_--){
            init();
            if(!N) {if(!yS[0]) puts("0");else puts("give me the bomb please");}
            else{
                if(!solve()) puts("give me the bomb please");
                else showAns(0),puts("");
            }
        }
        return 0;
    }
    View Code

     Different DigitsHDU - 1664 

    算法:数论+BFS

    思路:感觉这题集出得挺不错,每道题总与上一道题有关联。这一题和上一题一样,也是求N的正整数倍。所以也是余数下手。

    但是这题巧妙在于,最优解是,答案需尽量少不同数字构成且最小。

    这里牵扯的数论就是:

     很好理解:求10进制的余数,(*10+1)MOD,因为只加一个数字的求余存在一个循环,存在0则为答案。余数相同,就互减得到答案,互减将余数减掉就是N的整数倍了。

    所以一个整数一定存在一个正整数倍的数,且由0和1的数构成。

    所以,两不同数字就构成本题答案;

    那么这题剩下就是求一个最优最小解,即不同数字的数量相同求最小。

    分两层,第一层只用一个数叠加求,得到答案就是最优。得不到去第二层,循环从不同两个数字组合求出答案。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    const int inf = 2e9;
    
    int N,ansLen;
    bool vis[65536];
    string ans,res;
    
    bool gO(int x){
        memset(vis,false,sizeof vis);
        int tmp = 0;res = "";
        while(1){
            tmp = (tmp*10+x) % N;
            if(vis[tmp]) return false;
            vis[tmp] = true;
            res += '0' + x;
            if(!tmp){
                if(ansLen == inf||ans.length()>res.length()){
                    ans = res;ansLen = res.length();return true;
                }
                return false;
            }
        }
    }
    
    int val[65536],pre[65536],cnt[65536];
    
    void getAns(int x){
        if(x==-1) return ;
        getAns(pre[x]);
        res += '0' + val[x];
    }
    
    bool gT(int x,int y){
        memset(vis,false,sizeof vis);
        memset(pre,-1,sizeof pre);
        memset(cnt,0,sizeof cnt);
        queue<int> q;
        int fk,num[2]={x,y};
        if(x) { fk = x%N;vis[fk] = true,val[fk] = x,cnt[fk] = 1;q.push(fk); }
        fk = y%N; if(!vis[fk]) {vis[fk] = true,val[fk] = y,cnt[fk] = 1;q.push(fk);}
        while(!q.empty()){
            int now = q.front();q.pop();
            if(cnt[now]>ansLen) return false;
            for(int i=0;i<2;i++){
                fk = (now*10 + num[i]) % N;
                if(vis[fk]) continue;
                vis[fk] = true,val[fk] = num[i],cnt[fk] = cnt[now] + 1,pre[fk] = now;
                if(!fk){
                    res = "";getAns(0);
                    if(ansLen==inf||ans.length()>res.length()||(ans.length()==res.length()&&res<ans)){
                        ans = res;ansLen = res.length();
                        return true;
                    }
                    return false;
                }
                q.push(fk);
            }
        }
        return false;
    }
    
    int main()
    {
        while(cin>>N&&N){
            int flag = 0; ansLen = inf;ans = "";
            for(int i=1;i<10;i++) if(gO(i)) flag = 1;
            if(flag) {cout<<ans<<endl;continue;}
            for(int i=0;i<10;i++) for(int j=i+1;j<10;j++) gT(i,j);
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code

     


     Pusher HDU - 2821 

    算法:枚举+dfs(简单模拟)

    题意:类似手机游戏,一副25*25的地图,有多个障碍物,障碍物由字母表示,a为一方块,b为内嵌的2方块,以此类推。

    球体,要选取一个地方起步(这里我用全地图枚举),进行弹射必须有一个空格当空隙。

    起步后,方向不变,直到撞到方块才停下,且推方块向后一步,方块数减一,剩余方块积累到后一步。后一步如果是地图外,则起步方块消失。

    如果球飞出地图,则失败。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    int n,m;
    int ditu[26][26],sum;
    
    int xx[4] = {0,0,1,-1};
    int yy[4] = {1,-1,0,0};
    char dire[5] = "RLDU";
    
    int len;
    char ans[2500];
    
    void init(){
        sum = 0,len=0;
        memset(ditu,0,sizeof ditu);
        for(int i=0;i<n;i++)for(int j=0;j<m;j++){
            char ch = getchar();
            while(ch==' '||ch=='
    ') ch = getchar();
            if(ch>='a'&&ch<='z') ditu[i][j] += ch - 'a' + 1, sum+= ditu[i][j];
        }
    }
    bool check(int a,int b,int c){
        int x = a + xx[c] , y = b + yy[c];
        if(x<0||x>=n||y<0||y>=m||ditu[x][y]!=0) return false;
        return true;
    }
    
    int check3(int x,int y){
        if(x<0||x>=n||y<0||y>=m) return 0;
        if(ditu[x][y]!=0) return 1;
        return 2;
    }
    
    bool check2(int &a,int &b,int c){
        while(1){
            a += xx[c],b += yy[c];
            int tnt = check3(a,b);
            if(tnt==0) return false;
            if(tnt==1) return true;
        }
    }
    
    bool dfs(int x,int y,int z){
        if(z==0) return true;
        for(int i=0;i<4;i++){
            if(!check(x,y,i)) continue;
            int a = x,b = y;
            if(!check2(a,b,i)) continue;
            //cout<<x<<" "<<y<<" "<<a<<" "<<b<<" "<<z<<endl;
            bool flag = false;
            if(check3(a+xx[i],b+yy[i])!=0) flag = true;
            int fk = ditu[a][b],fkk,c;
            if(flag){
                fkk = ditu[a+xx[i]][b+yy[i]];
                ditu[a+xx[i]][b+yy[i]] += fk - 1;
                c = z - 1;
            }else c = z - fk;
            ditu[a][b] = 0;
            ans[len++] = dire[i];
            if(dfs(a,b,c)) return true;
            len--;
            ditu[a][b] = fk;
            if(flag) ditu[a+xx[i]][b+yy[i]] = fkk;
        }
        return false;
    }
    
    int main()
    {
        while(cin>>m>>n){
            init();
            //cout<<sum<<endl;
            //for(int i=0;i<n;i++){for(int j=0;j<m;j++) cout<<ditu[i][j]<<" ";cout<<endl;}
            int cp = 1;
            for(int i=0;i<n&&cp;i++)for(int j=0;j<m&&cp;j++) if(ditu[i][j]==0&&dfs(i,j,sum)) {
                    printf("%d
    %d
    ",i,j);
                    ans[len] = '';cout<<ans<<endl;
                    cp = 0;
            }
        }
        return 0;
    }
    View Code

     Tempter of the Bone IIHDU - 2128 

    算法:BFS/DFS

    题意:就是给一个有障碍的地图,图上有炸弹推,经过就可以捡上,不过只能捡一次。炸弹可以炸墙,不过算一步。

    判重就简单的3维,坐标加炸弹。


     BFS

    这里我BFS,直接模拟,找到答案直接输出,然后wrong了。

    后面看别人代码,得知原因,因为,我直接将炸开炸弹,与进入墙体两步,结合在一层进行模拟;

    容易导致BFS跨步错误,就像之前做过的一道题,情侣被鬼追,G - Nightmare Ⅱ  HDU - 3085 。这题我在kuangbin题集做了解析。

    这里类似2步走,第二步vis标记会导致其他第一步入队而出现答案错误,wrong。

    这里我是看别人代码学的操作,将vis移到出队再加,然后答案,通过ans记录,并进行求最短剪枝。这里就相当于将vis标记放到下一层去判断,虽然会增加入队冗余,但是ans优化也减了一部分。

    一开始码的时候不知道这样对不对?不管先试了再说。然后就AC了。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    const int inf = 2e9;
    
    int n,m,ans;
    int Sx,Sy;
    
    char ditu[9][9];
    bool vis[9][9][600];
    
    int xx[] = {1,-1,0,0};
    int yy[] = {0,0,1,-1};
    
    void init(){
        memset(vis,false,sizeof vis);
        ans = inf;
        for(int i=0;i<n;i++)for(int j=0;j<m;j++){
            char ch = getchar();while(ch==' '||ch=='
    ') ch = getchar();
            ditu[i][j] = ch;
            if(ch=='S') Sx = i,Sy = j;
        }
    }
    
    struct node{
        int x,y,step,exp;
        bool state[9][9];
        node(int a,int b,int c,int d):x(a),y(b),step(c),exp(d) {
            memset(state,false,sizeof state);
        };
    };
    
    bool bfs(){
        queue<node> q;q.push(node(Sx,Sy,0,0));
        while(!q.empty()){
            node p = q.front();q.pop();
            vis[p.x][p.y][p.exp] = true;
            if(p.x>=ans) continue;
            for(int i=0;i<4;i++){
                node now = p;
                now.x = p.x + xx[i] , now.y = p.y + yy[i],now.step++;
                if(now.x<0||now.x>=n||now.y<0||now.y>=m) continue;
                if(ditu[now.x][now.y]=='D') {ans = min(ans,now.step);continue;}
                if(ditu[now.x][now.y]=='X'&&!now.state[now.x][now.y]){
                    if(now.exp==0) continue;
                    else now.state[now.x][now.y] = true,now.exp--,now.step++;
                }
                if(ditu[now.x][now.y]>='0'&&ditu[now.x][now.y]<='9'&&!now.state[now.x][now.y]){
                    now.state[now.x][now.y] = true;
                    now.exp += ditu[now.x][now.y] - '0';
                }
                if(vis[now.x][now.y][now.exp]) continue;
                q.push(now);
            }
        }
        if(ans==inf) return false;
        else {cout<<ans<<endl;return true;}
    }
    
    int main()
    {
        while(cin>>n>>m&&n&&m){
            init();
            if(!bfs()) puts("-1");
        }
        return 0;
    }
    View Code

    后面想验证一下,到底是不是炸弹并步走,才导致的wrong,我就尝试将炸弹和入墙分为两步,再不同层进行。结果也AC。所以之前wrong就是BFS跨步走问题。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    
    using namespace std;
    
    const int inf = 2e9;
    
    int n,m;
    int Sx,Sy;
    
    char ditu[9][9];
    bool vis[9][9][600];
    
    int xx[] = {1,-1,0,0};
    int yy[] = {0,0,1,-1};
    
    void init(){
        memset(vis,false,sizeof vis);
        for(int i=0;i<n;i++)for(int j=0;j<m;j++){
            char ch = getchar();while(ch==' '||ch=='
    ') ch = getchar();
            ditu[i][j] = ch;
            if(ch=='S') Sx = i,Sy = j;
        }
    }
    
    struct node{
        int x,y,step,exp;
        int state[9][9];
        node(int a,int b,int c,int d):x(a),y(b),step(c),exp(d) {
            memset(state,0,sizeof state);
        };
    };
    
    bool bfs(){
        queue<node> q;q.push(node(Sx,Sy,0,0));vis[Sx][Sy][0] = true;
        while(!q.empty()){
            node p = q.front();q.pop();
            if(ditu[p.x][p.y]=='X'&&p.state[p.x][p.y]==2){
                p.state[p.x][p.y]=1;
                p.step++;
                q.push(p);
                continue;
            }
            for(int i=0;i<4;i++){
                node now = p;
                now.x = p.x + xx[i] , now.y = p.y + yy[i],now.step++;
                if(now.x<0||now.x>=n||now.y<0||now.y>=m) continue;
                if(ditu[now.x][now.y]=='D') {cout<<now.step<<endl;return true;}
                if(ditu[now.x][now.y]=='X'&&now.state[now.x][now.y]==0){
                    if(now.exp==0) continue;
                    else {
                        now.state[now.x][now.y] = 2,now.exp--;
                        if(vis[now.x][now.y][now.exp]) continue;
                        vis[now.x][now.y][now.exp] = true;
                        q.push(now);continue;
                    }
                }
                if(ditu[now.x][now.y]>='0'&&ditu[now.x][now.y]<='9'&&!now.state[now.x][now.y]){
                    now.state[now.x][now.y] = 1;
                    now.exp += ditu[now.x][now.y] - '0';
                }
                if(vis[now.x][now.y][now.exp]) continue;
                vis[now.x][now.y][now.exp] = true;
                q.push(now);
            }
        }
        return false;
    }
    
    int main()
    {
        while(cin>>n>>m&&n&&m){
            init();
            if(!bfs()) puts("-1");
        }
        return 0;
    }
    View Code

     IDEA*  

    这里我DFS模拟,我决策函数用哈夫曼函数。然后模拟与上面BFS无疑,重点这里DFS也需要一步步来,因为想求得即最优。

    可能存在没有答案,所以limit需要上限,8*8 = 64,64-2 = 62;然后取7个空作9炸弹,剩下55个空都是墙,然而63>55,所以55个墙为最坏情况,63+55 = 108步。上限为108;

    (这里最坏情况假设,每次拿最近的炸弹必须用完之前的,每细想其他细节,所以大概就估算)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    #include <cmath>
    
    using namespace std;
    
    const int inf = 2e9;
    
    int n,m;
    int Sx,Sy,Fx,Fy;
    int limit,nextd;
    
    char ditu[9][9];
    bool vis[9][9][600];
    int state[9][9];
    
    int xx[] = {1,-1,0,0};
    int yy[] = {0,0,1,-1};
    
    void init(){
        memset(vis,false,sizeof vis);
        memset(state,0,sizeof state);
        for(int i=0;i<n;i++)for(int j=0;j<m;j++){
            char ch = getchar();while(ch==' '||ch=='
    ') ch = getchar();
            ditu[i][j] = ch;
            if(ch=='S') Sx = i,Sy = j;if(ch=='D') Fx = i,Fy = j;
        }
    }
    
    int check(int x,int y){
        return abs(x-Fx) + abs(y-Fy);
    }
    
    bool dfs(int x,int y,int step,int exp,int limit){
        int h = check(x,y);
        if(h+step>limit){
            nextd = min(h+step,nextd);
            return false;
        }
        if(h==0) {cout<<step<<endl;return true;}
        if(ditu[x][y]=='X'&&state[x][y]==2){
            state[x][y] = 1;
            if(dfs(x,y,step+1,exp,limit)) return true;
            return false;
        }
        for(int i=0;i<4;i++){
            int a = x + xx[i],b = y + yy[i],c = step+1,d = exp;
            if(a<0||a>=n||b<0||b>=m) continue;
            int flag = 0;
            if(ditu[a][b]=='X'&&!state[a][b]){
                if(d==0) continue;
                else{
                    d--,state[a][b] = 2,flag = 1;
                    if(vis[a][b][d]) continue;
                    vis[a][b][d] = true;
                    if(dfs(a,b,c,d,limit)) return true;
                    vis[a][b][d] = false;
                    if(flag) state[a][b] = 0;
                    continue;
                }
            }
            if(ditu[a][b]>='0'&&ditu[a][b]<='9'&&!state[a][b]) state[a][b] = 1,flag = 1,d += ditu[a][b] - '0';
            if(vis[a][b][d]) continue;
            vis[a][b][d] = true;
            if(dfs(a,b,c,d,limit)) return true;
            vis[a][b][d] = false;
            if(flag) state[a][b] = 0;
        }
        return false;
    }
    
    int main()
    {
        while(cin>>n>>m&&n&&m){
            init();
            int flag = 0;vis[Sx][Sy][0] = true;
            for(limit = check(Sx,Sy);limit<=102;){
                nextd = inf;
                if(dfs(Sx,Sy,0,0,limit)) {flag = 1;break;}
                limit = nextd;
            }if(flag) continue;
            puts("-1");
        }
        return 0;
    }
    View Code

     Ali and BabaHDU - 4101 

    算法:BFS + 博弈判奇偶

    难点:题目在于理解。因为博弈,两人都是以自己最优的思路操作。所以胜负关键点很重要。

    胜负关键点在于,谁最后打开宝藏包围圈谁输。

    题意:一副地图,只有一个宝藏,然后存在石头推(以数字表示,<100),然后每人每步可以打碎一个石头。然后走路不算操作数。

    思路:第一步:先标记从宝藏出发能达到的0点(包括宝藏自身也要标记),因为到达这里就表示已经赢了;这里如果标记点,存在于边界,就表示Ali不用破碎石头就能拿宝藏,所以Ali Win;

       第二步:获取走到胜负关键点的总操作数。(胜负关键点就是谁可以走到那些上一步的标记点,因为谁都不想帮别人破最后的石头,所以就会选择破其他无关石头,所以要记录所有外围石头数即总操作数);这里简单联想一下,就是这一步宝藏外围一定有一个石头包围圈即屏障,谁先打破谁输。

       第三步:第二步的重点在于记录石头数量,因为最后屏障是一个石头数量1的包围圈,这个包围圈石头数不用记录;然后所有外围石头数量总和判奇偶,奇数表示Baba要去破壁,所以Ali Win;反之同义。

    失误:这里我wrong了;有两点:1)Baba Win 我写成了BaBa Win;2)第二步,我只用了(0,0)入队判断,其实不行,因为包围圈可能已经覆盖到边界了。所以要所有边界点入队判断;

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    #include <cmath>
    
    using namespace std;
    
    int n,m,Sx,Sy,ans;
    
    int ditu[303][303];
    bool vis[303][303],vis2[303][303];
    int xx[] = {1,-1,0,0};
    int yy[] = {0,0,1,-1};
    
    void init(){
        memset(vis,false,sizeof vis);
        memset(vis2,false,sizeof vis2);
        ans = 0;
    }
    
    struct node{
        int x,y;
        node(int a,int b):x(a),y(b) {};
    };
    
    bool bfs(){
        queue<node> q;
        q.push(node(Sx,Sy));vis2[Sx][Sy] = true;
        while(!q.empty()){
            node p = q.front();q.pop();
            if(p.x==0||p.x==n-1||p.y==0||p.y==m-1) return true;
            for(int i=0;i<4;i++){
                int x = p.x + xx[i],y = p.y + yy[i];
                if(x<0||x>=n||y<0||y>=m||ditu[x][y]!=0||vis2[x][y]) continue;
                q.push(node(x,y));
                vis2[x][y] = true;
            }
        }
        return false;
    }
    
    bool check(int x,int y){
        for(int i=0;i<4;i++) if(vis2[x+xx[i]][y+yy[i]]) return true;
        return false;
    }
    
    void bfs2(){
        queue<node> q;
    
        for(int j=0;j<m;j++){
            if(!vis[0][j]) q.push(node(0,j)),vis[0][j] = true,ans += ditu[0][j];
            if(!vis[n-1][j]) q.push(node(n-1,j)),vis[n-1][j] = true,ans += ditu[n-1][j];
        }
        for(int i=0;i<n;i++){
            if(!vis[i][0]) q.push(node(i,0)),vis[i][0] = true,ans += ditu[i][0];
            if(!vis[i][m-1]) q.push(node(i,m-1)),vis[i][m-1] = true,ans += ditu[i][m-1];
        }
    
        while(!q.empty()){
            node p = q.front();q.pop();
            if(check(p.x,p.y)) {ans --;continue;}
            for(int i=0;i<4;i++){
                int x = p.x + xx[i],y = p.y + yy[i];
                if(x<0||x>=n||y<0||y>=m||vis[x][y]) continue;
                q.push(node(x,y));
                vis[x][y] = true,ans+=ditu[x][y];
            }
        }
        //cout<<ans<<endl;
        if(ans%2) puts("Ali Win");
        else puts("Baba Win");
    }
    
    void show(){
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++) cout<<vis2[i][j]<<" ";cout<<endl;
        }
    }
    
    int main()
    {
        while(~scanf("%d%d",&n,&m)){
            init();
            for(int i=0;i<n;i++) for(int j=0;j<m;j++){
                scanf("%d",&ditu[i][j]);
                if(ditu[i][j]==-1) Sx = i,Sy = j;
            }
            if(bfs()){
                puts("Ali Win");
            }else bfs2();
            //show();
        }
        return 0;
    }
    View Code

     Ancient Messages HDU - 3839 

    算法:BFS

    题意:就是给你一副图,图里面有很多埃及象形字,然后图像由像素0,1构成,1表示颜色黑,0则表示白。

    难点:第一点在于怎么巧妙区别埃及象形字?   每个埃及象形字都是有闭合空间并且数量对于每个埃及象形字不同,所以可以从空白入手。

       第二点在于如何区分埃及象形字内的空白与外空白?  这里题目给出了关键点,就是每两个埃及象形字必不连接且不嵌套。并且黑色部分永远相连不断。

    思路:预处理:这里使用十六进制代表二进制像素,所以需要还原。

       第一步:从边界开始读空白,进行白色BFS,把所有埃及象形字的外空白标记。

       第二步:从上到下从左到右,找埃及象形字,遇到则开始黑色BFS,每遇到白色没标记则开始白色BFS标记;重点记录,遇到几次空白,即这个埃及象形字有多少闭合空白空间。从而区分这个是什么埃及象形字;

         第三步:通过BFS得出的埃及象形字数量,然后字典输出即可AC。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <queue>
    #include <map>
    #include <cmath>
    
    using namespace std;
    
    int n,m,ditu[202][202],ans[8],cas=1;
    
    int gg[] = {1,5,3,2,4,0};
    char gk[] = {'A','D','J','K','S','W'};
    
    int xx[] = {1,-1,0,0};
    int yy[] = {0,0,1,-1};
    
    bool vis[202][202];
    
    struct node{
        int x,y;
        node(int a,int b):x(a),y(b) {};
    };
    
    void bfs_white(int a,int b){
        queue<node> q;
        q.push(node(a,b)),vis[a][b] = true;
        while(!q.empty()){
            node p = q.front();q.pop();
            for(int i=0;i<4;i++){
                int x = p.x + xx[i],y = p.y + yy[i];
                if(x<0||x>=n||y<0||y>=m||ditu[x][y]||vis[x][y]) continue;
                q.push(node(x,y)),vis[x][y] = true;
            }
        }
        return ;
    }
    
    void bfs_black(int a,int b){
        queue<node> q;
        int acer = 0;
        q.push(node(a,b)),vis[a][b] = true;
        while(!q.empty()){
            node p = q.front();q.pop();
            for(int i=0;i<4;i++){
                int x = p.x + xx[i],y = p.y + yy[i];
                if(x<0||x>=n||y<0||y>=m||vis[x][y]) continue;
                if(ditu[x][y]) q.push(node(x,y)),vis[x][y] = true;
                else bfs_white(x,y),acer++;
            }
        }
        ans[acer]++;
        return ;
    }
    
    void show(){
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++) cout<<ditu[i][j]<<" ";cout<<endl;
        }
    }
    
    int main()
    {
        while(~scanf("%d%d",&n,&m)){
            if(!n&&!m) break;
            for(int i=0;i<n;i++) for(int j=0,z=0;j<m;j++){
                char ch = getchar();
                while(ch==' '||ch=='
    ') ch = getchar();
                int num;
                if(ch>='a'&&ch<='z') num = ch-'a'+10;
                else num = ch-'0';
                if(num>=8) ditu[i][z++] = 1,num-=8;
                else ditu[i][z++] = 0;
                if(num>=4) ditu[i][z++] = 1,num-=4;
                else ditu[i][z++] = 0;
                if(num>=2) ditu[i][z++] = 1,num-=2;
                else ditu[i][z++] = 0;
                if(num>=1) ditu[i][z++] = 1;
                else ditu[i][z++] = 0;
            }m*=4;
            //show();
    
            memset(ans,0,sizeof ans);
            memset(vis,false,sizeof vis);
    
            //边界找0
            for(int j=0;j<m;j++){
                if(!vis[0][j]&&ditu[0][j]==0) bfs_white(0,j);
                if(!vis[n-1][j]&&ditu[n-1][j]==0) bfs_white(n-1,j);
            }
            for(int i=0;i<n;i++){
                if(!vis[i][0]&&ditu[i][0]==0) bfs_white(i,0);
                if(!vis[i][m-1]&&ditu[i][m-1]==0) bfs_white(i,m-1);
            }
    
            //找图像
            for(int i=0;i<n;i++)for(int j=0;j<m;j++) if(!vis[i][j]&&ditu[i][j]) bfs_black(i,j);
    
            printf("Case %d: ",cas++);
            for(int i=0;i<6;i++) for(int j=0;j<ans[gg[i]];j++) printf("%c",gk[i]);
            puts("");
        }
        return 0;
    }
    View Code

       

  • 相关阅读:
    力扣leetcode 435. 无重叠区间
    C++中的快读和快写
    python安装wordcloud库报错
    python习题 随机密码生成 + 连续质数计算
    python 四位玫瑰数 + 100以内素数求和
    解决git add README.md 时报错 fatal: pathspec 'README.md' did not match any files
    洛谷P3817 小A的糖果 贪心思想
    多机部署之session共享解决方案-copy
    一点区块链资料-copy
    kafka集群搭建及结合springboot使用-copy
  • 原文地址:https://www.cnblogs.com/BugClearlove/p/13787680.html
Copyright © 2011-2022 走看看