本文主要是介绍Codejam之Alphabet Cake,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
问题描述
需要为party准备一个蛋糕,R行 C列的格子形状。助理已经把每个孩子的名字首字母写在了蛋糕的单元里,每个孩子的名字首字母都是唯一的,没有重复。每个单元至多有1个首字母。
切分蛋糕时,每个孩子的蛋糕都是矩形的,只包含自己的名字首字母,且不包含其他孩子的名字首字母。
输入:第一行的数字T,有T个测试用例。
每个测试用例开始是R和C,接着R行,每行有C个字符,表示开始的蛋糕状况。?号表示这个单元为空,英文字母表示助理在这个单元上写的孩子名字。
输出:蛋糕的切分情况,每个单元上的字母表示分给哪个孩子。每个孩子被分得的区域必须是矩形。
不能添加新的字母
如果有多个答案,输出其中任意一个都可以。
小数据集:1<=R<=12,1<=C<=12,R*C<=12
大数据集:1<=R<=25,1<=C<=25
问题解决
一开始的想法是遍历数组,如果cake i j 等于?,向右找第一个不等于?的字母,如果没有向上找,如果没有向左或下找。
这种方法得到的可能不是矩形。
??
?E
C?
得到
CC
EE
CE
遍历数组,如果cake i j不等于?,将它当作1by1的box,先上下拉伸,再左右拉伸,不能碰到其他字母。这种方法也可能失败。
如下面这个示例,结果有一个单元没有被填充。
A?B
C??
??D
?EF
得到
AAB
C?B
CDD
CEF
暴力法
小数据集中,一个蛋糕最多有12个单元,输入的蛋糕上有L个字母,已经占据了L个单元,那么还剩下12-L个单元。对每一个空白单元,有L个字母可以选择。
情况有L的(12-L)次方种,L在1到12之间,因此最大值是5的7次方=78125
对于每种情况进行检查,检查如下:
遍历数组,找到每个字母的最左上的位置和最右下的位置,出现次数。对每个字母,检查最左上位置和最右下位置构成的矩形是否只包含这个字母,且区域的大小等于出现次数。
分治法
如果一个蛋糕上只有一个字母,用这个字母填充完全。
如果一个蛋糕上有两个或两个以上字母,可以切分为两块更小的蛋糕。两块小蛋糕至少有一个字母。
为使两块小蛋糕至少有一个字母,遵循的切割规则:
1. 如果字母全部都在一列上,从中间切开。(这种情况再竖着切只会得到全是?的一块)
??A
???
??C
得到
??A
和
???
??C
2. 选择最左的字母所在列的右边切开
?A?
B??
??C
得到
?
B
?
和
A?
??
?C
int r = Integer.parseInt(aline.split(" ")[0]);
int c = Integer.parseInt(aline.split(" ")[1]);
char[][] cake = new char[r][c];
for(int i=0;i<r;i++) {aline=reader.readLine();for(int j=0;j<c;j++) {cake[i][j] = aline.charAt(j);}
}
divide_and_conquer(cake,0,0,r,c);
private static void divide_and_conquer(char[][] cake,int si,int sj,int rno,int cno) {//si和sj是矩形的左上点的索引,rno是行数(宽度),cno是列数(长度)//(si,sj) ... (si,sj+cno-1)//(si+rno-1,sj)... (si+rno-1,sj+cno-1)if(rno==1 && cno==1) return;//如果只有一个字符,用这个字符填充区域int lno = 0;char l = '?';for(int j=sj;j<=sj+cno-1;j++) {for(int i=si;i<=si+rno-1;i++) {if(cake[i][j]!='?') {lno++;l=cake[i][j];}}}if(lno==1) {for(int i=si;i<=si+rno-1;i++) {for(int j=sj;j<=sj+cno-1;j++) {cake[i][j]=l;}}return;}//如果全部字母存在一列上,从上到下找到第一个字母,从下到上找到最后一个字母,从两者中间切割,为保证切成的两块至少含有一个字母int llno = 0; //记录字母是否都在一列上int llrno = 0; //如果都在一列上,记录是都在哪一列上for(int j=sj;j<=sj+cno-1;j++) {for(int i=si;i<=si+rno-1;i++) {if(cake[i][j]!='?') {llno++;llrno=j;break;}}}if(llno==1) {int topj = si;int bottomj = si+rno-1;while(cake[topj][llrno]=='?')topj++;while(cake[bottomj][llrno]=='?')bottomj--;int medium = topj+(bottomj-topj)/2;divide_and_conquer(cake,si,sj,medium-si+1,cno);divide_and_conquer(cake,medium+1,sj,si+rno-1-medium,cno);return;}//找到最左的字母存在的列,从右边分割 for(int j=sj;j<=sj+cno-1;j++) {for(int i=si;i<=si+rno-1;i++) {if(cake[i][j]!='?') {divide_and_conquer(cake,si,sj,rno,j-sj+1);divide_and_conquer(cake,si,j+1,rno,cno-(j-sj+1));return;}}}
}
这篇关于Codejam之Alphabet Cake的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!