poj1128 拓扑排序+DFS

/**
 * poj1128 拓扑排序+深度遍历 
 * 这个题的思路还是比较有意思的,方块叠在方块上,让你找出来叠加的顺序。每个方块的四条边都肯定有没覆盖的点,这就很好确定每个方块的位置
 * 第一个关键点是,对于一个字母,你看谁在你的方块上,就能确定谁的叠加顺序就高于你,这就形成了一个有向图,利用拓扑排序就可以完成排序的操作
 * 这里简单叙述一下拓扑排序吧,就是给你一个有向图,将所有的点按照入度从小到大排序
 * 好吧,如果你和我一样也不知道入度啥意思,某个点的入度就是有向无环图里以该点为终点的边的个数
 * 做法是,每次找到一个入度为0的点,把与它相连的边去掉,也就是把跟它相连的点的入度-1,循环直到所有点都被排好
 * 但题目的要求是,可能会有多种情况,那单纯的拓扑排序就不能解决问题
 * 这就出现了第二个关键点,用沿字母序DFS的方法遍历所有可能的拓扑排序的路线,就可以输出所有可能的叠加顺序了
 * 
 * 这个题题目说的不清楚,样例给的也不好,实际上它有多组输入,一定要记得初始化,特别是输出的那个数组的初始化。我就是因为那个没做,WA了好多次
 */
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int MAX_LEN = 32;
const int LEFTTOP = 0,RIGHTBOTTOM = 1,X = 0,Y = 1;
const int TOP = 0,BOTTOM = 1,LEFT = 2,RIGHT = 3;
char input[MAX_LEN][MAX_LEN];
int corner[26][4];//一级索引表示字母,二级索引0表示方块最上行数,1表示最下行数,2表示最左列数,3表示最右列数
bool direction[26][26];
int in[26];//-1表示没有出现过这个字母
char output[26];


void dfs(char* s,int num,int max){
    if(num == max){
        printf("%s\n",s);
        return ;
    }

    for(int i=0;i<26;++i){
        if(in[i] == -1){
            continue;
        }
        //in[i]==0说明当前点为最底层点了
        if(in[i] == 0){
            in[i] = -1;
            for(int j=0;j<26;++j){
                if(direction[i][j]){
                    in[j]--;
                }
            }
            s[num] = i + 'A';
            dfs(s,num+1,max);
            //再恢复回来
            in[i] = 0;
            for(int j=0;j<26;++j){
                if(direction[i][j]){
                    in[j]++;
                }
            }
        }
    }
}

int main(){
    int h,w,acount;
    while(scanf("%d%d",&h,&w) != EOF){

        acount = 0;
        for(int i=0;i<h;++i){
            scanf("%s",input[i]);
        }

        //数组初始化
        for(int i=0;i<26;++i){
            output[i] = 0;
            in[i] = -1;
            corner[i][TOP] = 31;
            corner[i][LEFT] = 31;
            corner[i][BOTTOM] = -1;
            corner[i][RIGHT] = -1;
            for(int j=0;j<26;++j){
                direction[i][j] = false;
            }
        }

        //确定各字母方块的左上角和右下角位置
        int tmp;
        for(int i=0;i<h;++i){
            for(int j=0;j<w;++j){
                if(input[i][j] == '.'){
                    continue;
                }
                tmp = input[i][j] - 'A';
                if(in[tmp] == -1){
                    in[tmp] = 0;
                    acount++;
                }
                
                if(corner[tmp][TOP] > i){
                    corner[tmp][TOP] = i;
                }
                if(corner[tmp][LEFT] > j){
                    corner[tmp][LEFT] = j;
                }
                if(corner[tmp][BOTTOM] < i){
                    corner[tmp][BOTTOM] = i;
                }
                if(corner[tmp][RIGHT] < j){
                    corner[tmp][RIGHT] = j;
                }
            }
        }

        //逐方块查看哪些方块在它的上面
        for(int i=0;i<26;++i){
            if(in[i] == -1){
                continue;
            }
            //检查方框的横边(带角)
            for(int j=corner[i][LEFT];j<=corner[i][RIGHT];++j){
                tmp = input[corner[i][TOP]][j] - 'A';
                if(!direction[i][tmp] && i!=tmp){
                    ++in[tmp];
                    direction[i][tmp] = true;
                }
                tmp = input[corner[i][BOTTOM]][j] - 'A';
                if(!direction[i][tmp] && i!=tmp){
                    ++in[tmp];
                    direction[i][tmp] = true;
                }
            }
            //检查方框的竖边(不带角)
            for(int j=corner[i][TOP]+1;j<corner[i][BOTTOM];++j){
                tmp = input[j][corner[i][LEFT]] - 'A';
                if(!direction[i][tmp] && i!=tmp){
                    ++in[tmp];
                    direction[i][tmp] = true;
                }
                tmp = input[j][corner[i][RIGHT]] - 'A';
                if(!direction[i][tmp] && i!=tmp){
                    ++in[tmp];
                    direction[i][tmp] = true;
                }
            }
        }

        //dfs搜索拓扑排序路径并输出
        dfs(output,0,acount);
    }


    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值