pku_1128_Frame Stacking

这题在次证明了我的英语水平是多么烂~~

题意:

给一些用A-Z组成的窗框,它们相互覆盖,要求自下往上顺序输出覆盖顺序,如果有多种情况按字母顺序排序输出.

这题数据挺水的~理论上有些数据基本过不了~例如26字母的全排序:

15
18
AAABBBCCCDDDEEEFFF
A.AB.BC.CD.DE.EF.F
AAABBBCCCDDDEEEFFF
GGGHHHIIIJJJKKKLLL
G.GH.HI.IJ.JK.KL.L
GGGHHHIIIJJJKKKLLL
MMMNNNOOOPPPQQQRRR
M.MN.NO.OP.PQ.QR.R
MMMNNNOOOPPPQQQRRR
SSSTTTUUUVVVWWWXXX
S.ST.TU.UV.VW.WX.X
SSSTTTUUUVVVWWWXXX
YYYZZZ............
Y.YZ.Z............
YYYZZZ............
26!的全排列复杂度加上排序复杂度~天呢~

有几种情况要注意

1.多组输出,有不确定的情况全部输出,按字典顺序排列.
2.图中的的frame均会给出4条边的情况(一个顶点包括2条边),可以推断出frame的长度和位置.

例如这组数据:

9
8
AAAAAAAA
ABBBBBBA
ABCCCCBA
ABC..CBA
ABC..CBA
ABC..CBA
ABCCCCBA
ABBBBBBA
AAAAAAAA
输出是ABC的全排列,而不是: CBA,如果有被包含的框体不算覆盖,08年亚洲赛有一道最简单的题叫 Ugly Windows 求最上面的一个框,我就当按这个算了~读题的问题没办法啊~~~


因为四个边都会露出来,所以不用考虑这样的数据,直接就能求出框体的位置,相对简单很多:

9
8
AAAAAAAA
AB....BA
ABCCCCBA
ABC..CBA
ABC..CBA
ABC..CBA
ABCCCCBA
ABBBBBBA
AAAAAAAA

基本解题思想:

这道题是枚举构图+回溯拓扑排序的问题,没有什么变形

首先枚举每个点,求其每个字母的左上和右下的坐标:

inline void setPoint(int i,int j)
{
	int s=a[i][j]-'A';
	disp[s].num++;
	disp[s].x1=min(disp[s].x1,i);
	disp[s].y1=min(disp[s].y1,j);
	disp[s].x2=max(disp[s].x2,i);
	disp[s].y2=max(disp[s].y2,j);
}

//fun:init()
for(int i=0;i<n;++i) {
		for(int j=0;j<m;++j) {
			if(a[i][j]=='.')
				continue;
			setPoint(i,j);
		}
	}

根据坐标构图: 若字母X的FRAME存在对字母X检索边框,若该边框位置被字母N覆盖,这产生一条边N->X,并且X的入度加一.:

void setGraphC(int x1,int x2,int y,int x)
{
//检索横边
        char s='A'+x;
	for(int i=x1;i<=x2;++i) {
		int t=a[i][y]-'A';
		if(!_hash[t]&&a[i][y]!='.'&&a[i][y]!=s) {
			_hash[t]=true;
			graph[t][x]=1;
			dis[x]++;
		}
	}
}

void setGraphK(int y1,int y2,int xx,int x)
{
//检索竖边
 char s='A'+x;
	for(int i=y1;i<=y2;++i) {
		int t=a[xx][i]-'A';
		if(!_hash[t]&&a[xx][i]!='.'&&a[xx][i]!=s) {
			_hash[t]=true;
			graph[t][x]=1;
			dis[x]++;
		}
	}
}
void setGraph(int x)
{//检索FRAME边框
	dis[x]=0;
	memset(_hash,0,sizeof(_hash));
	int x1=disp[x].x1;
	int x2=disp[x].x2;
	int y1=disp[x].y1;
	int y2=disp[x].y2;
	setGraphC(x1,x2,y1,x);
	setGraphC(x1,x2,y2,x);
	setGraphK(y1,y2,x1,x);
	setGraphK(y1,y2,x2,x);
}

for(int i=0;i<26;++i)
	if(disp[i].num!=0)
		num++,setGraph(i);

构图完成后,就拓扑排序的步骤,进行回溯:

void dfs(vector<int> &aa,int cnt)
{
	if(cnt==num) {
		va.push_back(aa);//存储搜索到的数据
		return ;
	}
	for(int i=25;i>=0;--i) {
		if(dis[i]==-1) continue;
		if(dis[i]==0) {//删入度
			dis[i]=-1;
			for(int j=0;j<26;++j)
				if(graph[i][j])
					dis[j]--;
			aa[cnt]=i;    //存储可行点
			dfs(aa,cnt+1);//递归回溯
                        //还原状态
                        dis[i]=0;
			for(int j=0;j<26;++j)
				if(graph[i][j])
					dis[j]++;
		}
	}
}

最后进行排序输出.



源代码:

#include <myhead.h>
typedef struct _point
{
	int x1,y1;
	int x2,y2;
	int num;
}Point;     //存储坐标
const int N=33;
int n,m,num;    //长宽,用到的字符个数
bool _hash[N];  
char a[N][N];   //源数据
int dis[N];     //入度
int graph[N][N];//构图的邻接矩阵
Point disp[N];
vector<vector<int> > va;  //存储答案

inline void setPoint(int i,int j)
{
	int s=a[i][j]-'A';
	disp[s].num++;
	disp[s].x1=min(disp[s].x1,i);
	disp[s].y1=min(disp[s].y1,j);
	disp[s].x2=max(disp[s].x2,i);
	disp[s].y2=max(disp[s].y2,j);
}

void setGraphC(int x1,int x2,int y,int x)
{
	char s='A'+x;
	for(int i=x1;i<=x2;++i) {
		int t=a[i][y]-'A';
		if(!_hash[t]&&a[i][y]!='.'&&a[i][y]!=s) {
			_hash[t]=true;
			graph[t][x]=1;
			dis[x]++;
		}
	}
}

void setGraphK(int y1,int y2,int xx,int x)
{
	char s='A'+x;
	for(int i=y1;i<=y2;++i) {
		int t=a[xx][i]-'A';
		if(!_hash[t]&&a[xx][i]!='.'&&a[xx][i]!=s) {
			_hash[t]=true;
			graph[t][x]=1;
			dis[x]++;
		}
	}
}

void setGraph(int x)
{
	dis[x]=0;
	memset(_hash,0,sizeof(_hash));
	int x1=disp[x].x1;
	int x2=disp[x].x2;
	int y1=disp[x].y1;
	int y2=disp[x].y2;
	setGraphC(x1,x2,y1,x);
	setGraphC(x1,x2,y2,x);
	setGraphK(y1,y2,x1,x);
	setGraphK(y1,y2,x2,x);
}
//1
void init()
{
	num=0;
	memset(dis,-1,sizeof(dis));
	memset(graph,0,sizeof(graph));
	for(int i=0;i<26;++i) {
		disp[i].num=0;
		disp[i].x1=disp[i].y1=N;
		disp[i].x2=disp[i].y2=0;
	}

	for(int i=0;i<n;++i)
		scanf("%s",a[i]);

	for(int i=0;i<n;++i) {
		for(int j=0;j<m;++j) {
			if(a[i][j]=='.')
				continue;
			setPoint(i,j);
		}
	}

	//2

	for(int i=0;i<26;++i)
		if(disp[i].num!=0)
			num++,setGraph(i);
}

inline void delVetx(int x)
{
	for(int i=0;i<26;++i)
		if(graph[x][i])
			dis[i]--;
}
//3

void dfs(vector<int> &aa,int cnt)
{
	if(cnt==num) {
		va.push_back(aa);
		return ;
	}
	for(int i=25;i>=0;--i) {
		if(dis[i]==-1) continue;
		if(dis[i]==0) {
			dis[i]=-1;
			for(int j=0;j<26;++j)
				if(graph[i][j])
					dis[j]--;
			aa[cnt]=i;
			dfs(aa,cnt+1);
			dis[i]=0;
			for(int j=0;j<26;++j)
				if(graph[i][j])
					dis[j]++;
		}
	}
}

bool cmp(vector<int> aa,vector<int> bb)
{// 排序时注意逆向排序,因为要求自底向上输出结果,在这里卡了半小时...手生啦~~~
	size_t mm=num-1;
	while(aa[mm]==bb[mm]&&mm!=0)
		mm--;
	return aa[mm]<bb[mm];
}

void getResult()
{
	//4
	sort(va.begin(),va.end(),cmp);
	vector<int> t;
	for(size_t i=0;i<va.size();++i) {
		t=va[i];
		for(int j=num-1;j>=0;j--) {
			printf("%c",t[j]+'A');
		}
		puts("");
		t.clear();
	}
	for(size_t i=0;i<va.size();++i)
		va[i].clear();
	va.clear();
}

int main()
{
	while(~scanf("%d%d",&n,&m)) {
		init();
		//5
		vector<int> aa(27);
		//6
		dfs(aa,0);
		//7
		getResult();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值