【字符串-拓扑or爆搜】USA4.4——重叠的图像Frame Up

3 篇文章 0 订阅
1 篇文章 0 订阅

前言

这道题和上一题(追查坏牛奶)一样——非常超级无敌恶心难做QAQ!(其实是自己太弱)

从早上9点开始码代码、下午3点半开始交题到晚上AC...我现在已经神志恍惚...

很有趣的是,有一个数据长得很清奇,就像在你debug正心力憔悴的时候对你——竖了个中指(小别致长得真东西

题目

题目描述

看下面的五张 9 x 8 的图像:

........   ........   ........   ........   .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

现在,把这些图像按照 1—5 的编号从下到上重叠,第 1 张在最下面,第 5 张在最顶端。如果一张图像覆盖了另外一张图像,那么底下的图像的一部分就变得不可见了。我们得到下面的图像:

.CCC....
ECBCBB..
DCBCDB..
DCCC.B..
D.BBBB.A
DDDDAD.A
E...AAAA
EEEEEE..

对于这样一张图像,计算构成这张图像的矩形图像从底部到顶端堆叠的顺序。

下面是这道题目的规则:

矩形的边的宽度为 1 ,每条边的长度都不小于 3 。

矩形的每条边中,至少有一部分是可见的。注意,一个角同时属于两条边。

矩形用大写字母表示,并且每个矩形的表示符号都不相同。

输入格式

第一行 两个用空格分开的整数:图像高 H (3 <= H <=30) 和图像宽 W (3 <= W <= 30) 。

第二行到第 H+1 行 H 行,每行 W 个字母。

输出格式

按照自底向上的顺序输出字母。如果有不止一种情况,按照字典顺序输出每一种情况(至少会有一种合法的顺序)。

输入输出样例

输入

9 8
.CCC....
ECBCBB..
DCBCDB..
DCCC.B..
D.B.ABAA
D.BBBB.A
DDDDAD.A
E...AAAA
EEEEEE..

输出

EDABC

说明/提示

题目翻译来自NOCOW。

USACO Training Section 4.4

题目大意

有一堆中空的矩形框框,一个框框由一种大写字母构成且一种字母只出现一次,

框框宽为1,边长至少为3,把他们任意叠放在一起

求从底层至最上层的框框顺序,有多个答案就按字典序输出

分析

要认真读题啊!最开始没看到:

【矩形的每条边中,至少有一部分是可见的。注意,一个角同时属于两条边】这个信息,

于是毫无思路,甚至怀疑此题是否可做qwq...

首先,因为“一个角同时属于两条边”,于是我们保存每个矩形的左上角坐标与右下角坐标,就确定了整个矩形

然后,可以容易看出,最上层的矩形是完整的,于是它可以作为我们的突破口

(一)从上层往底层,逐层搜索完整的矩形

1.按字符从大到小搜索矩形

Ps.因为答案要求字典序,是按底层到上层从小到大的,我是从上层到底层,所以要从大到小

2.当该矩形只含' . '或该矩形的字母时,就是合法的

3.若矩形合法,就将其字符记录下来(我的做法是放入栈里),然后全部变成'.',意思是删除该矩形

4.回溯:将当层删去的矩形恢复成删前的样子,注意不是恢复成输入时的样子(这一条很重要,影响到答案的正确性!)

(二)输出答案

1.将所有答案按字典序排序

2.快乐输出

(三)其他

1.对于多个字符串进行字典序排序的方法

因为习惯用char来搞字符串,于是从某个地方学到了qsort的玄学操作(详见上面WA代码),似乎是关于指针,还挺好用,只是要背一背打法

后来请求某Tao大佬时学到了:原来string特别好用,可以直接sort,连cmp都不用打,厉害了

2.关于 char 和 string 的各种玄学

AC代码的前身——全部用string,结果答案出现了许多奇怪的字符

AC代码——ans[  ],a[  ][  ]用的char,其余string

我觉得应该是 string 与 char 直接使用a[ i ][ j ]这种单个字符时有些区别,得注意一下

最开始的玄学WA代码

/*
ID:lunasmi2
TASK:frameup
LANG:C++                 
*/
#include<cstdio>
#include<cmath>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=50,INF=0x3f3f3f3f;
int dir1[4]={1,0,-1,0},dir2[4]={0,1,0,-1};
bool vis[MAXN+5],ok[MAXN+5];
char a[MAXN+5][MAXN+5],c[MAXN+5],ans[MAXN+5],tmp[MAXN+5],Ans[100000+5][MAXN+5];
int h,w,cnt,cnt_ans;
struct node
{
	int x1,y1,x2,y2;
}t[MAXN+5];
bool cmp(int a,int b)
{
	return a>b;
}
int CMP(const void *a,const void *b)
{
	char *s1=(char *)a;
	char *s2=(char *)b;
	return strcmp(s1,s2);
}
void print()//debug用,可忽略
{
	for(int i=1;i<=h;i++)
		printf("%s\n",a[i]+1);
	printf("\n--------------\n");
}
bool dfs(int x,int y,int C,int d)
{
	if(x==t[C].x1&&y==t[C].y1&&d==3)
	{
		a[x][y]='.';
		return true;
	}
	if(!(a[x][y]=='.'||a[x][y]-'A'==C))
		return false;
	if(x==t[C].x2&&y==t[C].y1)
	{
		if(dfs(x,y+1,C,d+1))
		{
			a[x][y]='.';
			return true;			
		}
		else
			return false;		
	}
	else if(x==t[C].x2&&y==t[C].y2)
	{
		if(dfs(x-1,y,C,d+1))
		{
			a[x][y]='.';
			return true;
		}
		else
			return false;		
	}
	else if(x==t[C].x1&&y==t[C].y2)
	{
		if(dfs(x,y-1,C,d+1))
		{
			a[x][y]='.';
			return true;
		}
		else
			return false;		
	}
	else
	{
		if(dfs(x+dir1[d],y+dir2[d],C,d))
		{
			a[x][y]='.';
			return true;
		}
		else
			return false;
	}
}
void restore(int x,int y,int C,int d)
{
	a[x][y]=C+'A';
	if(x==t[C].x1&&y==t[C].y1&&d==3)
		return;
	if(x==t[C].x2&&y==t[C].y1)
		restore(x,y+1,C,d+1);
	else if(x==t[C].x2&&y==t[C].y2)
		restore(x-1,y,C,d+1);
	else if(x==t[C].x1&&y==t[C].y2)
		restore(x,y-1,C,d+1);
	else
		restore(x+dir1[d],y+dir2[d],C,d);
}
void DFS(int id,int tot)
{
	if(tot==cnt)
	{
		for(int i=tot-1;i>=0;i--)
			tmp[(tot-1)-i]=ans[i];
		memcpy(Ans[cnt_ans++],tmp,sizeof(tmp));
		return ;
	}
	for(int i=1;i<=cnt;i++)
	{
		if(!ok[i])
		{
			if(dfs(t[c[i]].x1,t[c[i]].y1,c[i],0))
			{
				ok[i]=1;
				ans[tot]=c[i]+'A';
				DFS(i,tot+1);
				restore(t[c[i]].x1,t[c[i]].y1,c[i],0);
				ok[i]=0;
			}
		}		
	}
}
int main()
{
    //freopen("frameup.in","r",stdin);
    //freopen("frameup.out","w",stdout);
    scanf("%d%d",&h,&w);
    for(int i=0;i<26;i++)
    	t[i].x1=t[i].y1=INF,t[i].x2=t[i].y2=0;
    for(int i=1;i<=h;i++)
    {
    	scanf("%s",a[i]+1);
    	for(int j=1;j<=w;j++)
    	if(a[i][j]!='.')
    	{
    		int tmp=a[i][j]-'A';
    		if(i<t[tmp].x1||j<t[tmp].y1)
    			t[tmp].x1=min(t[tmp].x1,i),t[tmp].y1=min(t[tmp].y1,j);
    		if(i>t[tmp].x2||j>t[tmp].y2)
    			t[tmp].x2=max(t[tmp].x2,i),t[tmp].y2=max(t[tmp].y2,j);
    		if(!vis[tmp])
    		{
    			vis[tmp]=1;
    			c[++cnt]=tmp;
			}
		}
	} 
	sort(c+1,c+cnt+1,cmp);
	DFS(-1,0);
	qsort(Ans,cnt_ans,sizeof(char)*55,CMP);
	for(int i=0;i<cnt_ans;i++)
		printf("%s\n",Ans[i]);
	return 0;
}
/*
5 7 
AAA.CCC 
A.A.C.C 
AABBBCC 
..B.B.. 
..BBB.. 
-------
4 4
CCC.
CACA
CCCA
.AAA
--------
4 8
CCC..BBB
C.CAABAB
C.CA.BBB
CCCAAAAA
--------
4 8
CCC..BBB
C.C..B.B
C.C..BBB
CCC.....
--------
*/

来之不易的AC代码

/*
ID:lunasmi2
TASK:frameup
LANG:C++                 
*/
#include<cstdio>
#include<cmath>
#include<stack>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=50,INF=0x3f3f3f3f;
int dir1[4]={1,0,-1,0},dir2[4]={0,1,0,-1};
bool vis[MAXN+5],ok[MAXN+5];
char ans[MAXN+5],a[MAXN+5][MAXN+5];
int c[MAXN+5];//记录第i个矩形的字母(ascll码) 
int h,w,cnt;
vector<string> Ans;//记录最终答案并用来排字典序 
stack<char> base;//储存旧图信息,方便回溯 
struct node
{
	int x1,y1,x2,y2;
}t[MAXN+5];
bool cmp(int a,int b)
{
	return a>b;
}
bool dfs(int x,int y,int C,int d)//检查是否是完整的矩形框,若是则全部变成'.' 
{
	if(!(a[x][y]=='.'||a[x][y]-'A'==C))//'.'和属于该矩形的字母都做合法字符 
		return false;
        //以下是走到四个矩形角上的点和一般点的情况 
	if(x==t[C].x1&&y==t[C].y1&&d==3)
	{
		base.push(a[x][y]),a[x][y],a[x][y]='.';
		return true;
	}
	if(x==t[C].x2&&y==t[C].y1)
	{//如果搜到最后矩形合法,则存下当前字符(回溯要用),当前字符变为'.',即删去该矩形
                if(dfs(x,y+1,C,d+1))
		{
			base.push(a[x][y]),a[x][y],a[x][y]='.';
			return true;			
		}
		else
			return false;		
	}
	else if(x==t[C].x2&&y==t[C].y2)
	{
		if(dfs(x-1,y,C,d+1))
		{
			base.push(a[x][y]),a[x][y],a[x][y]='.';
			return true;
		}
		else
			return false;		
	}
	else if(x==t[C].x1&&y==t[C].y2)
	{
		if(dfs(x,y-1,C,d+1))
		{
			base.push(a[x][y]),a[x][y],a[x][y]='.';
			return true;
		}
		else
			return false;		
	}
	else
	{
		if(dfs(x+dir1[d],y+dir2[d],C,d))
		{
			base.push(a[x][y]),a[x][y],a[x][y]='.';
			return true;
		}
		else
			return false;
	}
}
void restore(int x,int y,int C,int d)
{
	//base是逆时针存的,现在顺时针将沿途字符变为弹出的字符,恢复为旧图  
	a[x][y]=base.top();base.pop();
	if(x==t[C].x1&&y==t[C].y1&&d==3)
		return;
	if(x==t[C].x2&&y==t[C].y1)
		restore(x,y+1,C,d+1);
	else if(x==t[C].x2&&y==t[C].y2)
		restore(x-1,y,C,d+1);
	else if(x==t[C].x1&&y==t[C].y2)
		restore(x,y-1,C,d+1);
	else
		restore(x+dir1[d],y+dir2[d],C,d);
}
void DFS(int id,int tot)
{
	if(tot==cnt)
	{
		string tmp;
		for(int i=tot-1;i>=0;i--)//因为是从上层往底层搜索的,所以倒着存答案 
			tmp+=ans[i];
		Ans.push_back(tmp);
		tmp.clear();
		return ;
	}
	for(int i=1;i<=cnt;i++)
	{
		if(!ok[i])
		{
			if(dfs(t[c[i]].x1,t[c[i]].y1,c[i],0))
			{
				ok[i]=1;
				ans[tot]=c[i]+'A';
				DFS(i,tot+1);
				restore(t[c[i]].x1,t[c[i]].y1,c[i],0);//恢复删去的矩形 
				ok[i]=0;
			}
		}		
	}
}
int main()
{
    //freopen("frameup.in","r",stdin);
    //freopen("frameup.out","w",stdout);
    cin>>h>>w;
    for(int i=0;i<50;i++)
    	t[i].x1=t[i].y1=INF,t[i].x2=t[i].y2=0;
    for(int i=0;i<h;i++)
    {
    	scanf("%s",a[i]);
    	for(int j=0;j<w;j++)
	    if(a[i][j]!='.')
	    {
	    	int tmp=a[i][j]-'A';
	    	t[tmp].x1=min(t[tmp].x1,i);
			t[tmp].y1=min(t[tmp].y1,j);
	    	t[tmp].x2=max(t[tmp].x2,i);
			t[tmp].y2=max(t[tmp].y2,j);
	    	if(!vis[tmp])
	    	{
	    		vis[tmp]=1;
	    		c[++cnt]=tmp;
			}
		}    	
	} 
	sort(c+1,c+cnt+1,cmp);//因为从上层往下层搜索,所以顺序从大到小,答案才会从小到大  
	DFS(-1,0);
	sort(Ans.begin(),Ans.end());//整体按字典序排一遍 
	for(int i=0;i<Ans.size();i++)
			cout<<Ans[i]<<endl;
	return 0;
}

总结

1.学到了【对于多个字符串进行字典序排序的方法】

2.思维不够全面,最开始有点偷懒..._(:зゝ∠)_...

回溯时直接把矩阵变成该矩阵的字母,以为不会有问题,结果出错了,调了好久qwq...

多亏某Tao大佬找到了hack数据,我才终于得以AC...pwp...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值