您的位置:首页 > 其它

POJ1128 Frame Stacking【DFS,拓扑排序】

2016-05-27 21:20 447 查看
题目:

Description

Consider the following 5 picture frames placed on an 9 x 8 array.

........ ........ ........ ........ .CCC....

EEEEEE.. ........ ........ ..BBBB.. .C.C....

E....E.. DDDDDD.. ........ ..B..B.. .C.C....

E....E.. D....D.. ........ ..B..B.. .CCC....

E....E.. D....D.. ....AAAA ..B..B.. ........

E....E.. D....D.. ....A..A ..BBBB.. ........

E....E.. DDDDDD.. ....A..A ........ ........

E....E.. ........ ....AAAA ........ ........

EEEEEE.. ........ ........ ........ ........

1 2 3 4 5

Now place them on top of one another starting with 1 at the bottom and ending up with 5 on top. If any part of a frame covers another it hides that part of the frame below.

Viewing the stack of 5 frames we see the following.

.CCC....

ECBCBB..

DCBCDB..

DCCC.B..

D.B.ABAA

D.BBBB.A

DDDDAD.A

E...AAAA

EEEEEE..

In what order are the frames stacked from bottom to top? The answer is EDABC.

Your problem is to determine the order in which the frames are stacked from bottom to top given a picture of the stacked frames. Here are the rules:

1. The width of the frame is always exactly 1 character and the sides are never shorter than 3 characters.

2. It is possible to see at least one part of each of the four sides of a frame. A corner shows two sides.

3. The frames will be lettered with capital letters, and no two frames will be assigned the same letter.

Input

Each input block contains the height, h (h<=30) on the first line and the width w (w<=30) on the second. A picture of the stacked frames is then given as h strings with w characters each.

Your input may contain multiple blocks of the format described above, without any blank lines in between. All blocks in the input must be processed sequentially.

Output

Write the solution to the standard output. Give the letters of the frames in the order they were stacked from bottom to top. If there are multiple possibilities for an ordering, list all such possibilities in alphabetical order, each one on a separate line.
There will always be at least one legal ordering for each input block. List the output for all blocks in the input sequentially, without any blank lines (not even between blocks).

Sample Input

9

8

.CCC....

ECBCBB..

DCBCDB..

DCCC.B..

D.B.ABAA

D.BBBB.A

DDDDAD.A

E...AAAA

EEEEEE..

Sample Output

EDABC

思路:

题意:每个图片由同一字母组成的边框表示,每个图片的字母都不同;

     在一个最多30*30的区域放置这些边框,这些边框叠在一起,给出从上向下看的图。每个相框保证四条边上至少有一点出现。

求底层向顶层叠放的边框次序,多种结果按字典序输出

     注:每个图片的四边都会有字符显示,其中顶点显示两边。

题解:题意的理解是难点,题目对图片的范围确定说得有点含糊不清。题目最需要理解的是下方的三句话。

     第一句和数据范围就确定了图片尺寸;

     第二句话提示读者应该考虑记录对角线上的两个顶点,以此记录该图片的位置和尺寸;

     第三句话确定了图片的数量及可以标明该图片的key值(字母)

     最后要注意Input中有多组样例,而Ouput中的多组次序需要按照字典序输出。

     理清题意后:接下来的工作就是先用两个顶点确立图片的位置和尺寸;

           接着利用各图片的位置和尺寸确定覆盖关系建立单向无环图(覆盖关系用vector里的东西来说明);

           最后利用DFS的回溯完成字典序的拓扑排序即可。

代码:

#include<iostream>
#include<vector>

using namespace std;
#define maxH 30
#define maxW 30

int h,w;
int frame[maxH][maxW];//存储输入内容的frame表
int in[26];//入度数组
void dfs(int len);//函数声明
char ans[26];//为了输出而设置的字符数组
int totalNum;//出现的字母数
vector<int> covered[26];//每个字母曾经被哪些字母覆盖过
struct Letter{
int x;//在x行
int y;//在y列
Letter(){x=-1;y=-1;}
}L[26][2];  //最多26个字母哦,存储每个字母所在框的左上角坐标和右下角坐标

void getFrameAngle(){
//初始化入度数组
memset(in,-1,sizeof(in));//注意:只有-1和0可以用memset赋值
//如果要全赋为1的话,用memset不对。因为memset是按字节进行赋值的。4个字节的int,实际用memset赋完了的值为0x01010101,也就是16843009

//对frame进行遍历,找到每个字母所对应框的左上角点的坐标和右下角点的坐标
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
if(frame[i][j]=='.')
continue;
int letter=frame[i][j]-65;//减去'A'的ASCII码,因为题干说字母全是大写的
in[letter]=0;
if(L[letter][0].x==-1){//说明这个字母的框的左上角和右下角的位置还没有被初始化过
//将左上角和右下角的位置进行初始化
L[letter][0].x=i;
L[letter][0].y=j;
L[letter][1].x=i;
L[letter][1].y=j;
}
else{
L[letter][0].x=min(i,L[letter][0].x);
L[letter][0].y=min(j,L[letter][0].y);
L[letter][1].x=max(i,L[letter][1].x);
L[letter][1].y=max(j,L[letter][1].y);
}
}
}
}

void getSequenceRelation(){
//获得不同字母之间的前继和后继的关系(为了之后的拓扑排序),填入邻接矩阵map
//bottom的点指向up的点
for(int num=0;num<26;num++){
//如果该字母框没有初始化,说明没有该字母,跳过
if(L[num][0].x==-1){
continue;
}
totalNum++;//计算出现的字母数
//对每个字幕框进行遍历(注意:框内部的内容不能表示是否覆盖,因为B压在C下,C内也会有B)
for(int i=L[num][0].x;i<=L[num][1].x;i++){
//如果在上下的边框上,则y轴要全遍历
if(i==L[num][0].x||i==L[num][1].x){
for(int j=L[num][0].y;j<=L[num][1].y;j++){
if(frame[i][j]=='.')
continue;
//如果这个框没被覆盖的话,则应该从左上角到右下角都是这个字母
//如果出现不是这个字母的字母,那么新的字母应该在本来应该的字母之上
int newLetter=frame[i][j]-65;
if(num!=newLetter){
in[newLetter]++;//入度加一
covered[num].push_back(newLetter);//压入覆盖它的这个字母
}
}
}
//不在上下的边上,y轴只要看最小值和最大值就好了
else{
//y最小值
int j=L[num][0].y;
if(frame[i][j]=='.')
continue;
int newLetter=frame[i][j]-65;
if(num!=newLetter){
in[newLetter]++;//入度加一
covered[num].push_back(newLetter);//压入覆盖它的这个字母
}
//y最大值
j=L[num][1].y;
if(frame[i][j]=='.')
continue;
newLetter=frame[i][j]-65;
if(num!=newLetter){
in[newLetter]++;//入度加一
covered[num].push_back(newLetter);//压入覆盖它的这个字母
}
}
}
}
}

void dfs(int len){
if (len == totalNum){
ans[len] = '\0';
printf("%s\n", ans);
return;
}

for (int num = 0; num < 26; num++){
if(L[num][0].x==-1){
continue;
}
if (in[num] == 0){
ans[len] = num + 'A';
in[num]--;
//然后将覆盖它的所有点(也就是它指向的点,会有重复的)的入度减一
//使用unsigned,不然会出现warning: “<”: 有符号/无符号不匹配
//因为 covered[num] 是一个Vector容器 ,covered[num].size() 在容器说明中 被定义为: unsigned int 类型, 而i是int 类型 所以会出现: 有符号/无符号不匹配警告
for (unsigned int i = 0; i < covered[num].size(); i++)
in[covered[num][i]]--;
dfs(len + 1);
for (unsigned int i = 0; i < covered[num].size(); i++)
in[covered[num][i]]++;
in[num]++;
}
}

}

int main(){
while(cin>>h){
cin>>w;
cin.get();
//初始化frame图
for(int i=0;i<h;i++){
for(int j=0;j<w;j++){
frame[i][j]=cin.get();
}
cin.get();
}
getFrameAngle();
getSequenceRelation();
dfs(0);
memset(in,-1,sizeof(in));
memset(L,-1,sizeof(L));
totalNum=0;
for(int i=0;i<26;i++){
covered[i].clear();
}
}
return 0;
}






贴上几个我认为不错的代码:

1.这个思路跟我差不多,但是速度比我快,快在哪里呢

2.这个是求出了算是对的入度。(比如E->D->A,那么A的入度应该是2,才能保证程序正确。因为E被去除时,D和A的入度都会减一,然后D再被去除时,A的入度再减一)

而我的入度是被覆盖了多少次,就加多少次。所以在减入度时,要把vector.size一个个减掉。

当然,我的程序也可以改成算出对的入度。

将程序中所有的

if(num!=newLetter){
in[newLetter]++;//入度加一
covered[num].push_back(newLetter);//压入覆盖它的这个字母
}
改为:

if(num!=newLetter){
if(map[num][newLetter]!=1){
map[num][newLetter]=1;
in[newLetter]++;//入度加一
}
}



这里的map为邻接矩阵。

这样的话之后dfs用邻接矩阵做。这样的话就不要用vector了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: