public class Test {
public <E> List<Integer> getProperKey(int sumPicnumOld, Map<Integer, List<E>> bansiPicnumMap){
List<Integer> finalResult = new ArrayList<Integer>(4);
if(sumPicnumOld == 1){
finalResult.add(1);
return finalResult;
}
TreeSet<Integer> set = getSetByMap(sumPicnumOld, bansiPicnumMap);
//取出最大和最小的值
int maxPic=1 ;
if(set.size()>0){
// minPic = set.first();
maxPic =set.last();
}
//根据最多的版式图,获取最少的页数值。对奇数页和偶数页进行分别处理。
finalResult = minPage(sumPicnumOld, maxPic, set);
System.out.print("sumPicnumOld = "+sumPicnumOld+" :" );
for(int i=0 ; i<finalResult.size() ; i++){
System.out.print(finalResult.get(i)+" ");
}
System.out.println();
return finalResult ;
}
//从map中获取所有比sumPicnumOld少的版式key
private <E> TreeSet<Integer> getSetByMap(int sumPicnumOld,Map<Integer, List<E>> map){
TreeSet<Integer> set = new TreeSet<Integer>();
for (Integer key : map.keySet()) {
if(key<sumPicnumOld){
set.add(key);
}
}
return set;
}
//获取排版最少的页数
private List<Integer> minPage(int sumPicnumOld ,int maxPic , TreeSet<Integer> set ){
int divisor = sumPicnumOld/maxPic; //页数
int remainder = sumPicnumOld%maxPic; //余数
List<Integer> finalResult = new ArrayList<Integer>();
for(int i = 0 ; i<divisor; i++){
finalResult.add(maxPic);
}
/**
* 余数不为0: 首先判断set中是否存在该值,如果存在,则直接当做一页;
* 如果不存在,这进行二分,如果set中存在,则二分当做两页
* 如果二分不可以,则将剩余的图和maxPic一起作一次二分,如果存在,则保存,
* 如果不存在 则对remainder按照递减的方式排版:如 7 = 2+2+2+1 (假如只有1,2两种板式)
*/
if(remainder>0){
// if(divisor%2 !=0){ //奇数
if(set.contains(remainder)){
finalResult.add(remainder);
}else{
List<Integer> twosumList = TwoSum(set, remainder);
if(twosumList.size()>0){
finalResult.add(twosumList.get(0));
finalResult.add(twosumList.get(1));
}else{
int setValue = maxPic+remainder; //剩余图和一个最多的图一起,作一次twosum算法匹配;如果还是匹配不上...则重新排版。
twosumList = TwoSum(set, setValue);
if(twosumList.size()>0){
finalResult.remove(finalResult.size()-1); //先移除最后一个值
finalResult.add(twosumList.get(0));
finalResult.add(twosumList.get(1));
}else{
recurOfRemainder(remainder, set, finalResult);
}
}
}
// }
}else{ //余数为0 : 排版divisor页最多的板式
//divisor进行奇偶处理;偶数不处理,奇数则考虑maxPic能否在set集合中找到两个相加的等值如: 5=2+3
if(divisor%2 !=0){ //奇数
List<Integer> list = TwoSum(set,maxPic); //2SUM算法
if(list.size()>0){
finalResult.remove(finalResult.get(0));
finalResult.add(list.get(0));
finalResult.add(list.get(1));
}else{
if(maxPic!=1){ //set中有且只有一个1值,作特殊处理
finalResult.add(1);
}
}
}
}
return finalResult;
}
//对余数进行递归处理版式
public List<Integer> recurOfRemainder( int remainder,TreeSet<Integer> set, List<Integer> finalResult ){
if(remainder == 1){
finalResult.add(remainder);
return finalResult ;
}else{
Integer lowerOfremainder = set.lower(remainder);
finalResult.add(lowerOfremainder);
remainder -= lowerOfremainder;
recurOfRemainder(remainder, set,finalResult);
}
return finalResult;
}
//setValue 给定值
public List<Integer> TwoSum( TreeSet<Integer> set,int setValue){
List<Integer> list = new ArrayList<Integer>();
Iterator <Integer> it = set.iterator();
while(it.hasNext()){
list.add(it.next());
}
int i =0;
int setSize =set.size();
int k=0;
if(setSize>1){
k = setSize-1;
}
List<Integer> temp = new ArrayList<Integer>();
for( ;i<=k; ){
int p1 = list.get(i);
int p2 = list.get(k);
if(p1+p2 == setValue){
temp.add(p1);
temp.add(p2);
return temp;
} else if(p1 + p2 > setValue)
k--;
else
i++;
}
return temp;
}
public static void main(String[] args) {
Test test = new Test();
// TreeSet<Integer> set = new TreeSet<Integer>();
// set.add(1);
// set.add(3);
// set.add(4);
// set.add(7);
// set.add(7);
// set.add(11);
// set.add(13);
// set.add(14);
// set.add(17);
//
// List<Integer> finalResult = new ArrayList<Integer>();
// finalResult= test.TwoSum(set,22);
// for(int i =0 ; i<finalResult.size() ; i++){
// System.out.println(finalResult.get(i));
// }
Map<Integer, List<Integer>> bansiPicnumMap = new HashMap<Integer, List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
bansiPicnumMap.put(1, list);
// bansiPicnumMap.put(2, list);
// bansiPicnumMap.put(3, list);
bansiPicnumMap.put(4, list);
// bansiPicnumMap.put(5, list);
bansiPicnumMap.put(6, list);
bansiPicnumMap.put(8, list);
// bansiPicnumMap.put(9, list);
test.getProperKey(1, bansiPicnumMap);
test.getProperKey(2, bansiPicnumMap);
test.getProperKey(3, bansiPicnumMap);
test.getProperKey(4, bansiPicnumMap);
test.getProperKey(5, bansiPicnumMap);
test.getProperKey(6, bansiPicnumMap);
test.getProperKey(7, bansiPicnumMap);
test.getProperKey(8, bansiPicnumMap);
test.getProperKey(9, bansiPicnumMap);
test.getProperKey(10, bansiPicnumMap);
test.getProperKey(11, bansiPicnumMap);
test.getProperKey(12, bansiPicnumMap);
test.getProperKey(13, bansiPicnumMap);
test.getProperKey(14, bansiPicnumMap);
test.getProperKey(15, bansiPicnumMap);
test.getProperKey(16, bansiPicnumMap);
test.getProperKey(17, bansiPicnumMap);
test.getProperKey(18, bansiPicnumMap);
test.getProperKey(19, bansiPicnumMap);
test.getProperKey(20, bansiPicnumMap);
}
打印结果:
sumPicnumOld = 2 :1 1
sumPicnumOld = 3 :1 1 1
sumPicnumOld = 4 :1 1 1 1
sumPicnumOld = 5 :4 1
sumPicnumOld = 6 :4 1 1
sumPicnumOld = 7 :6 1
sumPicnumOld = 8 :6 1 1
sumPicnumOld = 9 :8 1
sumPicnumOld = 10 :8 1 1
sumPicnumOld = 11 :8 1 1 1
sumPicnumOld = 12 :8 4
sumPicnumOld = 13 :8 1 4
sumPicnumOld = 14 :8 6
sumPicnumOld = 15 :8 1 6
sumPicnumOld = 16 :8 8
sumPicnumOld = 17 :8 8 1
sumPicnumOld = 18 :8 8 1 1
sumPicnumOld = 19 :8 8 1 1 1
sumPicnumOld = 20 :8 8 4
排版需求:
参数:
sumPicnumOld :需要排版的图片数
Map<Integer, List<E>> bansiPicnumMap : key保存的是每个板式的图片数,value为对应的版式内容
要求:1.排版所需的页数最少;2 为偶数页 如果排版最后为奇数页,可以添加一个空白页
上述代码的思路:
1. 取出map中的key值,获取比sumPicnumOld 小的所有key值并进行排序,保存进TreeSet中。
TreeSet 的first() ,last() 方法很有用处
2.获取最少的页数
比如
sumPicnumOld = 19
treeSet包含 {1,3,5},
则排版的结果为:19=5+5+5+3+1
所以首先对set中的最大值,进行整除和求余
int divisor = sumPicnumOld/maxPic; //页数
int remainder = sumPicnumOld%maxPic; //余数
整除的结果就是上面例子中5的个数,余数为4。
此时有两种情况。
余数为0和余数不为0.
当余数为0时候,
如果为奇数,处理方案为:拿出最大值,进行二分处理,看看这个最大值在set集合中是否等于两个子集的和。
如果存在,则二分
如果不存在,那么直接加一个空白页。
余数不为0
/**
* 余数不为0: 首先判断set中是否存在该值,如果存在,则直接当做一页;
* 如果不存在,这进行二分,如果set中存在,则二分当做两页
* 如果二分不可以,则将剩余的图和maxPic一起作一次二分,如果存在,则保存,
* 如果不存在 则对remainder按照递减的方式排版:如 7 = 2+2+2+1 (假如只有1,2两种板式)
*/
remainder根据set递减,通过递归就可以实现,保存在list中,list当做参数传入就可以了;用到了TreeSet的lower(Object)方法。
另:
TreeSet类
TreeSet是SortedSet接口的实现类,可以确保集合元素处于排序状态。
TreeSet中的几个方法:
Object first():返回集合中的第一个元素。
Object last():返回集合中的最后一个元素。
Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参数元素不需要是TreeSet集合里的元素)。
Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参数元素不需要是TreeSet集合里的元素)。
SortedSet subSet(formElement,toElement):返回次Set的子集合,范围从formElement(包含)到toElement(不包含)。
SortedSet headSet(toElement):返回此Set的子集,由小于toElement的元素组成。
SortedSet tailSet(fromElement):返回此Set的子集,由大于或等于fromElement的元素组成。