题目大意
给定一个由带编号的积木搭成的长方体。其中每块积木的厚度都一样,由若干个单位边长的相邻方块组成(相邻是指两个方块有一面重合)。现在要求将这个长方体中的积木一块一块拆掉。每块积木只能从顶端取出,并且取出时不能移动还在长方体中的其它积木。请你给出一个拆积木的顺序。当然这种顺序可能是不唯一的,我们规定当有多种选择时,总是取出编号最小的那个。
输入格式:
输入第一行给出两个正整数 N 和 M(1≤N,M≤1000),分别为长方体的高度和宽度。随后 N 行,每行给出 M 个整数,为对应位置上积木的编号。编号为不超过 106 的正整数,数字间以空格分隔。题目保证不同积木的编号是不同的。
输出格式:
在一行中输出按照题目限定可以拆卸积木的顺序。数字间以 1 个空格分隔,行首尾不得有多余空格。如果拆卸到某一步发现无法继续了,则在对应位置上输出 Impossible
,然后结束。
输入样例 1(示意如上图):
解释
5 5 4 4 4 11 11 9 9 4 2 1 9 5 4 4 4 7 3 3 6 10 8 8 8 10 10
输出样例 1:
11 1 2 4 6 9 5 3 7 8 10
输入样例 2:
解释
4 3 8 9 7 4 4 4 5 6 4 1 4 4
输出样例 2:
7 8 9 Impossible
解题思路
题目大意很清晰,拆积木从顶端取出,并且取出时不能移动还在长方体中的其它积木。请你给出一个拆积木的顺序。当然这种顺序可能是不唯一的,我们规定当有多种选择时,总是取出编号最小的那个。很明显是拓扑排序(但是总是取出编号最小的那一个不难想到把入度为零的点加到小根堆里,这里用一个黑盒优先队列)那么如何建边呢就是给出的二维数组中的上边的点和紧挨着的下边,注意本题是个稠密图要用邻接边建边不然会超内存,稀疏图用邻接矩阵,
#include<iostream>
#include<algorithm>
#include<queue>
#include<string.h>
using namespace std;
const int N=1e3+5,M=1e6+5;
int a[N][N];
int h[M],e[M],ne[M],id;
int d[M];//节点的入边数
int q[M];
int v[M];
int st[M];
int n,m;
int hh=0,tt=-1;
int ans;//有多少个积木块
void add(int aa,int bb)
{
e[id]=bb;
ne[id]=h[aa];
h[aa]=id++;
}
void bfs()
{ priority_queue<int,vector<int>,greater<int>> heap;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(!d[a[i][j]])
{ if(!v[a[i][j]])
heap.push(a[i][j]);//先把入度为零的点入队注意不能有重复的点
v[a[i][j]]=1;//对入队的点进行标记一下防止有重复的点
}
}
}
if(heap.empty())//如果没有一个点的入度为零即没有可拆的木块直接输出
{
cout<<"Impossible";
return ;}
while(!heap.empty())//拓扑排序的模板
{
int u=heap.top();
heap.pop();
q[++tt]=u;
for(int i=h[u];i!=-1;i=ne[i])
{ int j=e[i];
d[j]--;
if(!d[j])
{ if(!v[j])
heap.push(j);
v[j]=1;
}
}
}
for(int i=0;i<tt;i++)
cout<<q[i]<<" ";
cout<<q[tt];
if(tt!=ans-1)
cout<<" "<<"Impossible";
}
int main()
{ memset(h,-1,sizeof h);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
st[a[i][j]]=1;
}
}
for(int i=1;i<=1e6;i++)
{
if(st[i])
ans++;
}
for(int i=1;i<n;i++)
{
for(int j=1;j<=m;j++)
{ if(a[i][j]==a[i+1][j])
continue;
add(a[i][j],a[i+1][j]);
d[a[i+1][j]]++;
}
}
bfs();
return 0;
}
下面看详细的代码