POJ 1128 Frame Stacking

题目大意:

        现有如下5个框架:

        

        其中每个框架由大写字母组成,只有边中间镂空,先将框架按照12345的顺序层层覆盖1在最底部5在最顶部,得到下图:

        

         现在从底到顶得到序列EDABC,现给定一副框架之间相互覆盖的图,图中保证:

         1. 一个框架必定只由一种字母组成,不会出现同一个字母的多个框架,并且每个框架的边长至少大于3;

         2. 每个框架都能看到其4个角;

         现有多个测例(测例以EOF结束),每个测例中都会给出图的规模h×w(h, w ≤ 30,行数为h,列数为w),接下来给出该图的矩阵,对于每个测例都要求输出从底到顶的序列,如果存在多个序列(有可能有两个部分没有相互覆盖),则按照字典序升序输出所有可能序列,测例之间不要用空行隔开。

题目链接

注释代码:

/*                                
 * Problem ID : POJ 1128 Frame Stacking
 * Author     : Lirx.t.Una                                
 * Language   : C++                    
 * Run Time   : 0 ms                                
 * Run Memory : 164 KB                                
*/

#include <iostream>
#include <cstring>
#include <cstdio>

//坐标的最大值(下标从0计,29最大)
#define	INF		30
//26个字母
#define	N		26
//框架的最大宽度
#define	MAXW	30

using namespace std;

char	m[MAXW][MAXW + 1];//存放框架信息,以%s形式逐行读入
//记录每种字母的最大和最小横坐标以确定其左上角和右下角
//下标从0 ~ 25表示A ~ Z
//注意x轴朝下,y轴朝右
char	x1[MAXW], x2[MAXW];//最小最大纵坐标
char	y1[MAXW], y2[MAXW];//最小最大横坐标

//思路:如果B盖在A上面就从A连向B一条边
//本题就转化成了拓扑排序问题了,下标从0 ~ 25表示A ~ Z
bool	g[N][N];//表示关系的图

char	deg[N];//各点的入度

char	path[N + 1];//最终所求序列
int		stp;//当前序列的长度,即步数,深搜时更新

int		cnt;//总共用到了多少个字母
//由于字母不是前多少个而是随机出现了,因此需要记录总共用到了
//多少个不同的字母

inline int
max( int a, int b ) {

	return a > b ? a : b;
}

inline int
min( int a, int b ) {

	return a < b ? a : b;
}

void
dfs(void) {//由于要按照字典序升序打印出所有可能因此使用深搜
	//每次搜索的方向都是以小字母为先,结果必定是升序的
	//由于题目保证一定有解因此不必特判

	int		u, v;//点

	if ( stp == cnt ) {//如果步数已经到达字母个数表示搜索成功
		//直接输出即可
	
		path[stp] = '\0';
		puts(path);
		return ;
	}

	for ( u = 0; u < N; u++ )//从小到大搜索
		if ( !deg[u] ) {//找到一个0入度的点就顺着该点向下纵深
		
			path[stp] = u + 'A';//记录到path中,需要从下标转换到相应的字母

			stp++;//步数增加
			deg[u]--;//将该点入度减到-1,避免在下一层深搜过程中重复使用该点
			for ( v = 0; v < N; v++ )//更新与之相连点的入度
				if ( g[u][v] ) deg[v]--;

			dfs();//进入下一层

			//搜索完毕后还原现场,再测试本层中下一个点
			for ( v = 0; v < N; v++ )
				if ( g[u][v] ) deg[v]++;
			deg[u]++;
			stp--;
		}
}

int
main() {

	int		h, w;//框架的高和宽
	int		i, j, k, c;//计数变量、临时变量
	int		i1, i2;//存放字母的纵坐标极值
	int		j1, j2;//存放字母的横坐标极值

	while ( ~scanf("%d%d", &h, &w) ) {
	
		//初始化
		memset(g, false, sizeof(g));
		memset(x1, INF, sizeof(x1));
		memset(y1, INF, sizeof(y1));
		memset(x2, -1, sizeof(x2));
		memset(y2, -1, sizeof(y2));
		memset(deg, -1, sizeof(deg));//注意要初始化成-1,因为有些点不用
		//如果初始化成0则那些不用的点也会被多算进去

		for ( i = 0; i < h; i++ ) scanf("%s", m[i]);//读入框架

		for ( k = 0; k < N; k++ )//逐个枚举检测每个字母k: 0 ~ 25 = A ~ Z
			for ( i = 0; i < h; i++ )
				for ( j = 0; j < w; j++ )
					if ( m[i][j] == k + 'A' ) {//获得极值

						x1[k] = min( x1[k], i );
						x2[k] = max( x2[k], i );
						y1[k] = min( y1[k], j );
						y2[k] = max( y2[k], j );
					}

		cnt = 0;//统计多少个字母被用到了
		for ( i = 0; i < N; i++ )
			if ( x1[i] != INF ) {
			
				cnt++;
				deg[i]++;//用到的字母入度置0
			}

		for ( k = 0; k < N; k++ )//枚举计算出每个字母的坐标极值
			if ( x1[k] != INF ) {
			
				i1 = x1[k];
				i2 = x2[k];

				j1 = y1[k];
				j2 = y2[k];

				for ( j = j1; j <= j2; j++ ) {
				
					c = m[i1][j] - 'A';
					if ( c != k && !g[k][c] ) {
					
						g[k][c] = true;
						deg[c]++;
					}

					c = m[i2][j] - 'A';
					if ( c != k && !g[k][c] ) {
					
						g[k][c] = true;
						deg[c]++;
					}
				}

				for ( i = i1; i <= i2; i++ ) {
				
					c = m[i][j1] - 'A';
					if ( c != k && !g[k][c] ) {
					
						g[k][c] = true;
						deg[c]++;
					}

					c = m[i][j2] - 'A';
					if ( c != k && !g[k][c] ) {

						g[k][c] = true;
						deg[c]++;
					}
				}
			}

		stp = 0;//初始化搜索步数
		dfs();
	}

	return 0;
}
无注释代码:

#include <iostream>
#include <cstring>
#include <cstdio>

#define	INF		30
#define	N		26
#define	MAXW	30

using namespace std;

char	m[MAXW][MAXW + 1];
char	x1[MAXW], x2[MAXW];
char	y1[MAXW], y2[MAXW];
bool	g[N][N];

char	deg[N];

char	path[N + 1];
int		stp;

int		cnt;

inline int
max( int a, int b ) {

	return a > b ? a : b;
}

inline int
min( int a, int b ) {

	return a < b ? a : b;
}

void
dfs(void) {

	int		u, v;

	if ( stp == cnt ) {
	
		path[stp] = '\0';
		puts(path);
		return ;
	}

	for ( u = 0; u < N; u++ )
		if ( !deg[u] ) {
		
			path[stp] = u + 'A';

			stp++;
			deg[u]--;
			for ( v = 0; v < N; v++ )
				if ( g[u][v] ) deg[v]--;

			dfs();

			for ( v = 0; v < N; v++ )
				if ( g[u][v] ) deg[v]++;
			deg[u]++;
			stp--;
		}
}

int
main() {

	int		h, w;
	int		i, j, k, c;
	int		i1, i2;
	int		j1, j2;

	while ( ~scanf("%d%d", &h, &w) ) {
	
		memset(g, false, sizeof(g));
		memset(x1, INF, sizeof(x1));
		memset(y1, INF, sizeof(y1));
		memset(x2, -1, sizeof(x2));
		memset(y2, -1, sizeof(y2));
		memset(deg, -1, sizeof(deg));

		for ( i = 0; i < h; i++ ) scanf("%s", m[i]);

		for ( k = 0; k < N; k++ )
			for ( i = 0; i < h; i++ )
				for ( j = 0; j < w; j++ )
					if ( m[i][j] == k + 'A' ) {

						x1[k] = min( x1[k], i );
						x2[k] = max( x2[k], i );
						y1[k] = min( y1[k], j );
						y2[k] = max( y2[k], j );
					}

		cnt = 0;
		for ( i = 0; i < N; i++ )
			if ( x1[i] != INF ) {
			
				cnt++;
				deg[i]++;
			}

		for ( k = 0; k < N; k++ )
			if ( x1[k] != INF ) {
			
				i1 = x1[k];
				i2 = x2[k];

				j1 = y1[k];
				j2 = y2[k];

				for ( j = j1; j <= j2; j++ ) {
				
					c = m[i1][j] - 'A';
					if ( c != k && !g[k][c] ) {
					
						g[k][c] = true;
						deg[c]++;
					}

					c = m[i2][j] - 'A';
					if ( c != k && !g[k][c] ) {
					
						g[k][c] = true;
						deg[c]++;
					}
				}

				for ( i = i1; i <= i2; i++ ) {
				
					c = m[i][j1] - 'A';
					if ( c != k && !g[k][c] ) {
					
						g[k][c] = true;
						deg[c]++;
					}

					c = m[i][j2] - 'A';
					if ( c != k && !g[k][c] ) {

						g[k][c] = true;
						deg[c]++;
					}
				}
			}

		stp = 0;
		dfs();
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值