zoj 1083 Frame Stacking 解题报告

文章介绍了如何利用拓扑排序、回溯和DFS算法解决一个关于框架堆叠的问题。题目要求根据输入的图像判断框架的堆叠顺序,作者通过创建图模型,建立边来表示遮挡关系,并进行拓扑排序以找到所有可能的堆叠序列。在程序实现中,使用了邻接表和栈来辅助处理,通过递归和回溯得到所有合法的序列。
摘要由CSDN通过智能技术生成


1.题目:

zoj 1083 Frame Stacking
题目大意:有几个大小不一的,且用不同字母表示的框架,他们可以堆叠起来,堆叠后底层的框架有部分将被遮挡。你的任务是读取一个堆叠后的图片,判断框架从底至顶的堆叠顺序。


2.解题思路:

这是一道拓扑排序+回溯+dfs的题。相比于一般的拓扑排序,它要求我们输出所有可能的序列。

首先我们需要用一个结构体表示框架,其中包含了框架的左上角和右下角坐标。在主函数中,一次输入一行字符串,然后对该字符串进行处理。如果发现字母,我们就需要对字母对应的框架进行更新,使这个框架大小尽可能大。

接下来是建立图模型,我们的任务是对已有的框架进行遍历:沿着边沿检查,如果出现其他字母,说明被覆盖了,该字母是其他字母的前置条件(至于为什么后面有提),建立从该字母到其他字母的边,在邻接表中记录,并使那个其他字母的入度加一。最后对图模型进行拓扑排序,由于需要输出所有情况,所有我们需要dfs和记录层数的deep,遇到入度为0的结点时进行递归,另外需要进行回溯,所以使用一个栈s来辅助,需要回退的数量用num记录,结果保存在ans。递归时也需要用到辅助数组opc记录已输出的字母,防止重复输出同个字母。


3.程序清单:

#include<bits/stdc++.h>
using namespace std;
struct frame
{
	int ltx,lty,rbx,rby;//左上角和右下角坐标
	frame(){ltx=-1;lty=-1;rbx=-1;rby=-1;}
}f[30];
int h,w,cnum,ai;
char p[40][40];
bool c[30],opc[30];//字母表和输出字母表
int indg[30];//入度表
set<int> adl[30];//邻接表
stack<int>s;
char ans[30];
void reset()
{
	while(!s.empty()) s.pop();
	for(int i=0;i<30;i++)
		adl[i].clear();
	ai=0;
	cnum=0;
	memset(c,0,sizeof(c));
	memset(f,-1,sizeof(f));
	memset(indg,0,sizeof(indg));
	memset(opc,0,sizeof(opc));
	memset(ans,0,sizeof(ans));
}
inline void edge(int u,int v)
{
	if(!adl[u].count(v))
	{
		adl[u].insert(v);
		indg[v]++;
	}
}
void create()
{
	for(int i=0;i<30;i++)
	{
		if(c[i])
		{
			int lx=f[i].ltx,rx=f[i].rbx,ty=f[i].lty,by=f[i].rby;
			for(int j=lx;j<=rx;j++)
			{
				if(p[j][ty]-'A'!=i)//被覆盖,建边
					edge(i,p[j][ty]-'A');
				if(p[j][by]-'A'!=i)
					edge(i,p[j][by]-'A');
			}
			for(int k=ty+1;k<=by-1;k++)//注意四个角已经判断过
			{
				if(p[lx][k]-'A'!=i)
					edge(i,p[lx][k]-'A');
				if(p[rx][k]-'A'!=i)
					edge(i,p[rx][k]-'A');
			}
		}
	}
}
void topsort(int deep)
{
	if(deep>cnum)
	{
		for(int k=0;k<ai;k++)
			printf("%c",ans[k]);
		printf("\n");
		return;
	}
	for(int i=0;i<30;i++)
	{
		if(c[i]&&!opc[i])
		{
			if(indg[i]==0)
			{
				ans[ai++]='A'+i;
				opc[i]=1;
				int num=adl[i].size();
				for(auto j:adl[i])
				{
					indg[j]--;
					s.push(j);
				}
				topsort(deep+1);
				while(num--)//回溯
				{
					indg[s.top()]++;
					s.pop();
				}
				ai--;
				opc[i]=0;
			}
		}
	}
}
inline void updatef(int k,int i,int j)//更新frame范围
{
	if(f[k].ltx==-1||f[k].ltx>i)
		f[k].ltx=i;
	if(f[k].rbx==-1||f[k].rbx<i)
		f[k].rbx=i;
	if(f[k].lty==-1||f[k].lty>j)
		f[k].lty=j;
	if(f[k].rby==-1||f[k].rby<j)
		f[k].rby=j;
}
int main()
{
	while(~scanf("%d%d",&h,&w))
	{
		for(int i=0;i<h;i++)
		{
			scanf("%s",p[i]);
			for(int j=0;j<w;j++)
			{
				if(p[i][j]!='.')
				{
					int k=p[i][j]-'A';
					updatef(k,i,j);
					if(c[k]!=1)//新结点
					{
						c[k]=1;
						cnum++;
					}
				}
			}
		}	
		create();
		topsort(1);
		reset();
	}
	return 0;
}

评测结果:
在这里插入图片描述


4.解题总结

这题对于一般的拓扑排序难度有些许提升,因为还要输出所有序列。还好经过条理分析之后写得比较顺利,只WA了一次。导致那一次WA的原因是一开始我没有设置ans数组,以为只要深搜,然后像一般拓扑一样满足条件就输出就行,后来发现想错了,还是需要对序列进行处理。另外对于拓扑排序,课件中的例子是为一些有前置课程的课程安排时间表,其实也可以活用于这题。我们可以将这题想象成一个物品堆叠问题,需要先取出顶部的物品才能取走底部的物品,只是我们的视角是从底部往上看的,这样,对于我们在这题中如何建立、为什么这样建立图模型就很容易理解了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值