前言
这道题和上一题(追查坏牛奶)一样——非常超级无敌恶心难做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...