组合长度poj1011
最近用使开发的过程中出现了一个小问题,顺便记载一下原因和方法--组合长度
c++的现实方法可以考参http://blog.csdn.net/lyy289065406/article/details/6647960
解题路思:
DFS+剪枝
POJ2362的强化版,重点在于剪枝
令InitLen为所求的最短原始棒长,maxlen为给定的棒子堆中最长的棒子,sumlen为这堆棒子的长度之和,那么InitLen必定在围范[maxlen,sumlen]中
根据棒子的灵巧度(棒子越长,灵巧度越低) DFS前先对全部棒子降序排序
剪枝:
1、 由于全部原始棒子等长,那么必有sumlen%Initlen==0;
2、 若能在[maxlen,sumlen-InitLen]找到最短的InitLen,该InitLen必也是[maxlen,sumlen]的最短;若不能在[maxlen,sumlen-InitLen]找到最短的InitLen,则必有InitLen=sumlen;
3、 由于全部棒子已降序排序,在DFS时,若某根棒子不合适,则跳过其面后全部与它等长的棒子;
4、 最主要的剪枝:对于某个目标InitLen,在每次构建新的长度为InitLen的原始棒时,检查新棒的第一根stick[i],若在搜索完全部stick[]后都法无组合,则说明stick[i]法无在后以组合方法下组合,不必往下搜索(往下搜索会令stick[i]被舍弃),直接返回上一层
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
static boolean[] used;//用于记载组适时木棒的用使情况
static int len;//截断后,木棒的根数
static int[] s;//用来存储截断后木棒的长度
static int sum;//全部木棒的总长度
static int max;//木棒的大最长度
static int parts;//理论上某种组合方法的木棒的根数
public static void main(String[] args) throws Exception {
BufferedReader read = new BufferedReader(new InputStreamReader(
System.in));
//断判输入的木棒的根数否是为0.
while ((len = Integer.parseInt(read.readLine()) ) != 0) {
//根据截断后木棒的根数来建创一个整型组数
s = new int[len];
StringTokenizer take = new StringTokenizer(read.readLine());
int index = 0;
sum = 0;
used = new boolean[len];
//hasMoreTokens() .测试此 tokenizer 的字符串中否是还有更多的可用记标。
while (take.hasMoreTokens()) {
//nextToken().返回此 string tokenizer 的下一个记标。
//将每一根木棒的长度存入s[]组数中
s[index] = Integer.parseInt(take.nextToken());
//盘算木棒的总长度
sum += s[index++];
}
//sort(int[] a) .对指定的 int 型组数按数字升序停止排序。
Arrays.sort(s);
//取得截断后木棒的大最长度
max = s[len - 1];
/**
*
*/
//在[max,sum]之间找寻initlen
for (; max <= sum; max++) {
//如果总长度sum对max整除
if (sum % max == 0) {
//盘算以max为原始长度,这时候应该有多少根木棒
parts = sum / max;
if (search(0, len - 1, 0)) {
System.out.println(max);
break;
}
}
}
}
}
/**
* search(int res, int next, int cpl)主要用于找寻最短的原始棒长
* 其主要思惟括包以下几点:
* 1)首先断判后以的组合长度res否是经已足满后以的原始棒长的可能值max,
* 如果足满了,将组合长度res置为0,next置为len-2,已组合的木棒根数cpl加1.
*
* 2)断判已组合的木棒根数cpl否是和理论上要组合木棒根数parts致一,
* 如果致一,这证明经已找到最短原始长度,返回true
*
* 3)如果进入到这一步,则证明还没有组合现实,这是重新开始组合一根木棒
* 1))首先断判否是还有棒子用于组合,如果没有,则执行接下来的false.
* 如果有,断判该棒子否是经已被用使过,如果被用使过,则用使下一根棒子,
* 如果没有被用使过,则断判组合长度res+该棒子的长度s[next]否是<=后以最短原始棒长的可能值max
* 如果不成立,则证明将剩下的棒子的长度都加起来,如果······
*
*
*
* @param res
* @param next
* @param cpl
* @return
*/
public static boolean search(int res, int next, int cpl) {
/**
* res:经已组合的长度
* next:要组合的木棒
* cpl:经已组合的木棒的根数
*/
/**
* 如果组合的长度经已到达木棒的大最长度
* 则停止下一轮组合:
* 组合长度res置0
* 经已组合好的木棒根数cpl的值+1
* 定确下一根要组合的木棒
*/
if (res == max) {
res = 0;
next = len - 2;
cpl++;
}
/**
* 如果经已组合好顶峰木棒数与理论上的等相,则
* 返回true
*/
if (cpl == parts) {
return true;
}
//从大到小开始组合
while (next >= 0){
//如果next对应的木棒还没有用使
if (used[next] == false) {
//如果后以组合长度小于木棒的大最长度
if (res + s[next] <= max){
//将后以木棒的用使属性识标为true
used[next] = true;
/**
* 续继组合,如果下一轮组合刚刚好足满件条,则
* 返回true,组合现实
*/
if (search(res + s[next], next - 1, cpl)) {
return true;
}
/**
* 如果面上的if语句没有执行则证明,还没有组合现实
*
*/
/**
* 如果执行到这里,则证明,后以木棒其实不合适用于组合
* 将后以经已用使到的木棒的用使属性记为false
*/
used[next] = false;
/**
* 对于新棒的第一根棒子如果在搜索万所搜棒子后以都法无组合
* ,则证明该棒子在后以组合方法下法无组合
*所以,直接返回上一层
*/
if (res == 0) {
break;
}
if (res + s[next] == max) {
break;
}
}
/**
*如果后以组合长度加上接下来的那一根木棒的长度大于大最长度
*,或者 经已现实这一次的组合,却还没有到达大最大最长度
*/
int i = next - 1;
/**
* while循环主要表现剪枝法:
* 如果后以木棒的长度不合适,那么
* 接下来的和后以木棒长度同相的木棒也不合适
*
*/
while (i >= 0 && s[i] == s[next]) {
i--;
}
next = i;
/**
* for循环的主要逻辑:
* 遍历接下来的剩下的全部木棒,
* 盘算剩下全部木棒的总长度l_s
*/
int l_s = 0;
for (int j = next; j >= 0; j--) {
if (!used[j]) {
l_s += s[j];
}
}
/**
* if循环的主要逻辑:
* 如果际实剩下全部木棒的总长度l_s<
* 还需要的木棒的长度,则,跳出循环
* 证明
*/
if (l_s < max - res) {
break;
}
/**
* 剩下的木棒的总长度>还需要的木棒的长度,则
* 续继组装
*/
continue;
}
//如果后以的木棒经已被用使过,将下标 - 1
next--;
}
//如果遍历完全部的木棒都没有组合功成,则
//返回false.
return false;
}
}
文章结束给大家分享下程序员的一些笑话语录:
腾讯的动作好快,2010年3月5日19时28分58秒,QQ同时在线人数1亿!刚刚看到编辑发布的文章,相差才2分钟,然后连专题页面都做出来了,他们早就预料到了吧?(其实,每人赠送10Q币,轻轻松松上两亿!)