zz: http://www.strongczq.com/2012/04/tco2012round1b-1-foxandkgram.html
题目原文:http://community.topcoder.com/stat?c=problem_statement&pm=11809&rd=15091
题目原文:http://community.topcoder.com/stat?c=problem_statement&pm=11809&rd=15091
题目大意:
利用int[] len表示一系列铅笔的长度,利用这些铅笔组成k-gram图案。k-gram图案的定义如下:
- 拥有k行
- 每行为一根长度为k的铅笔或者两个长度和为k-1的铅笔
问:最大的k取值为多少。
数据规模:铅笔数为[1,50],每根铅笔的长度为[1,50]。
思路:
数据规模很小,所以直接从大到小遍历k的可能取值。显然k不会超过铅笔的个数,所以遍历范围为len.length到1。对于每一种k尝试构造该k-gram图。构造过程也比较简单,外层循环遍历每一根铅笔,如果正好长度为k则增加一行,如果长度小于k-1则通过内层循环寻找一个互补的铅笔共同构造一行,过程中用数组boolean[] used保存铅笔是否已使用的信息。如果最终构造的行数>=k则构造成功。该算法的计算复杂度为O(n^3),n为铅笔数。
如果铅笔数的数据规模比较大,可以考虑对构图过程进行优化。使用一个数组int[] number表示各种长度的笔的个数,构造的计算复杂度为O(n)。然后遍历number的每一种长度,如果正好为k则将个数加到行数上;如果小于k-1则可以O(1)查找互补铅笔的个数,取二者个数的最小值。使用该构造方式后算法的计算复杂度优化为O(n*m),其中m为铅笔的最大长度。
Java代码:
public class FoxAndKgram
{
public int maxK( int[] len)
{
int n = len.length ;
for (int k = n; k >= 1; --k){
int cnt = 0;
boolean [] used = new boolean[n];
for (int i = 0; i < n; ++i) if(!used[i]){
used[i] = true ;
if (len[i] == k){
cnt++;
} else if (len[i] < k - 1){
for (int j = i + 1; j < n; ++j) if(!used[j]){
if (len[j] + len[i] == k - 1){
used[j] = true ;
cnt++;
break ;
}
}
}
}
if (cnt >= k){
return k;
}
}
return 0;
}
}