Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 4133 | Accepted: 1388 |
Description
........ ........ ........ ........ .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
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
Sample Input
9 8 .CCC.... ECBCBB.. DCBCDB.. DCCC.B.. D.B.ABAA D.BBBB.A DDDDAD.A E...AAAA EEEEEE..
Sample Output
EDABC
来对比一下这题和poj2585,两道题的本质是一样的,都是覆盖问题。但这道题目的难度却远大于poj2585,难在什么地方呢?
首先poj2585那道题目,如果把2*2的矩阵看成框架的话,其大小是固定的,并且位置也是固定的,这样在扫描(i,j)能被哪些框架覆盖,就简单很多了。但是这道题目框架的大小和位置都不固定,只给了你一幅最终的图,让你复原出原图像,这绝对是一大挑战。
因此我们要抓住题目给的一些特殊条件,第一只会出现大写字母,并且每个框架的字母是相同的,不同框架的字母是不同的,这个也就是告诉了我们最多有26个框架。
再来挖个条件,题目告诉每个框架的四条边都会有一部分被看见,四个顶点算作两条边(因为是交点)。这个条件极其重要,有了它我们可以把每个框架的四条边的坐标给确定了。譬如框架A,那么我们扫描整幅图,用u,l,d,r记录元素A出现位置的最上,最左,最下,最右坐标。
知道了四条边的位置,这个框架无非就是将四条边延长,相交成一个矩形。也就是我们可以得到A框架会出现在整幅图的哪些位置。然后模仿poj2585,我们统计(i,j)会出现哪些字母。
之后的做法还是模仿poj2585,建图一幅DAG,这道题是必定有解的,必定无环。
这样还不够,题目还要输出所有解,而且按字典序从小到大排列,光拓扑排序得不到所有的拓扑序列。我们只能深刻挖掘拓扑排序算法的本质了。想一想栈(或队列)里存在的元素之间会有覆盖关系吗?实际上是不可能的。
因为第一次入栈的0度点,肯定是没有覆盖关系的,而从栈里出来一些点,删掉他们所带的边,会得到一些新的0度点,这些点肯定与栈里还遗留的点没有边啊,因为那些点还没出栈,因此那些点所发出的边还未删除,这样如果两者有边,边一定还没被删除,而现在入栈的是0度点啊,显然矛盾,因此没关系。
既然栈里的所有元素之间,两两都没有覆盖关系,也就是顺序是可以任意的,也就是可以全排列。但我们知道栈里遗留的元素是随出栈顺序而动态变化的,因此我们采用dfs,枚举出栈顺序,动态统计排列个数。而题目要求是字典序从小到大,那么我们直接规定dfs枚举的顺序为A~Z即可。
但是此题还是很坑,它输出的序列是反向的,最底层的在序列的最前,最上层的在序列的最后。那么我们枚举得从最底层框架开始枚举了,这样建图方式也得改下,将poj2585覆盖关系反一下,poj2585中a覆盖b,建图是连a->b,这题就必须连b->a了。
从这题来看,一个好的算法,不应该只局限于它的表面作用,应该深入挖掘它的本质,只有这样才能更好应对难题。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
char mat[40][40],ans[30];
int u[30],d[30],l[30],r[30];
vector<int> maz[40][40];
int adj[30][30];
int indeg[30],exist[30],vis[30];
int tot;
void dfs(int cur,int dp){
ans[dp]='A'+cur;
if(dp==tot){
ans[dp+1]='\0';
printf("%s\n",ans+1);
return;
}
vis[cur]=1;
for(int i=0;i<26;i++)
if(exist[i]&&!vis[i]&&adj[cur][i])
indeg[i]-=adj[cur][i];
for(int i=0;i<26;i++)
if(exist[i]&&!vis[i]&&!indeg[i])
dfs(i,dp+1);
for(int i=0;i<26;i++)
if(exist[i]&&!vis[i]&&adj[cur][i])
indeg[i]+=adj[cur][i];
vis[cur]=0;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++)
scanf("%s",mat[i]);
for(int i=0;i<26;i++)
u[i]=l[i]=30,d[i]=r[i]=-1;
memset(exist,0,sizeof exist);
tot=0;
for(int i=0;i<n;i++) //确定上下边界
for(int j=0;j<m;j++){
if(mat[i][j]=='.') continue;
int c=mat[i][j]-'A';
if(!exist[c]) exist[c]=1,tot++; //统计字母是否存在,字母的个数
u[c]=min(u[c],i);
l[c]=min(l[c],j);
d[c]=max(d[c],i);
r[c]=max(r[c],j);
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
maz[i][j].clear();
for(int i=0;i<26;i++){ //构造每点的覆盖
if(!exist[i]) continue;
for(int j=l[i];j<=r[i];j++){
maz[u[i]][j].push_back(i);
maz[d[i]][j].push_back(i);
}
for(int j=u[i]+1;j<=d[i]-1;j++){ //+1,-1是为了防止顶点压入两次
maz[j][l[i]].push_back(i);
maz[j][r[i]].push_back(i);
}
}
memset(adj,0,sizeof adj);
memset(indeg,0,sizeof indeg);
for(int i=0;i<n;i++) //构造邻接矩阵(反向构图)
for(int j=0;j<m;j++)
for(int k=0;k<maz[i][j].size();k++)
if(mat[i][j]-'A'!=maz[i][j][k]){
adj[maz[i][j][k]][mat[i][j]-'A']++;
indeg[mat[i][j]-'A']++;
}
memset(vis,0,sizeof vis);
for(int i=0;i<26;i++)
if(exist[i]&&!indeg[i]) dfs(i,1);
}
return 0;
}