本题心路历程
(错解)拿到题目,以为很简单,想直接暴力搜,以体重数组为宽度,以小猫数量为深度,设定book数组记录,如果这个小猫被放进车里,记录1,那下一次跳过遍历,每一次搜索整个数组,如果把小猫放入不超载就放,一直到遍历到数组尾部,加一辆车;
便有了下面的错误代码。
package dfs;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class 小猫下山 {
static int w;
static int cnt;
static int n;
static int num=0;
static int sum;
static Integer[] a=new Integer[18];
static int book[]=new int [18];
static int flag=0;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
w=sc.nextInt();
for(int i=0;i<n;i++)
{
a[i]=sc.nextInt();
}
Arrays.sort(a,0,n-1,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2-o1;
}
}) ;
dfs(1);
System.out.println(cnt);
sc.close();
}//应该排个序,先排小的,最后排大的
//cur现在小猫的个数
//sum现在小猫的重量
//m 记录n
//版本1,能加就加,
static boolean dfs(int cur)
{
if(num>=n) return true;
if(flag==1) {cnt++; //System.out.println(cur+"#"+num+"@"+sum+"%%");
num=num+cur-1;sum=0;flag=0;return dfs(1);}
for(int i=0;i<n;i++)
{
if(i==n-1) {flag=1; }//这个flag一定要放在最上面,标记数组最后一只小猫
if(sum+a[i]>w) continue;
if(book[i]==1) continue;
//if(i==n-1) flag=1; 错误的位置 应该写在上面,不然最后一个小猫体重大,就不会执行这行语句
sum+=a[i];//能装则装
// System.out.println(sum+"**");
book[i]=1;
dfs(cur+1);
if(num>=n) return true;//很关键,我找到答案,后面都不用回溯了
book[i]=0;
int t=sum;
sum-=a[i];
// System.out.println(t+"-"+a[i]+"="+sum); 测试用 忽略就好
}
if(flag==1) {cnt++; //System.out.println(cur+"#"+num+"@"+sum+"%%");//测试
num=num+cur-1;sum=0;flag=0;return dfs(1);}//在最后加一个判断,接住i=N-1的情况,防止最后一个是数太大直接continue进不去递归了
return false;
}
}
前面案例都能过,直到我遇到了这样一组案例
6.。。。。。。
错误原因,没有枚举到所有情况,局部最优不等于全局最优。。。。
所以,请看方案二
可以直接把重量大的小猫放到新的车里,那么每拿到一只小猫,我们可以把它放到前cnt辆车里,或者新开一辆车。
直到达到递归边界,与之前记录的情况求个最小值,这样就能遍历到所有的情况。
ac代码。
package dfs;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class 小猫下山修正 {
static int w;
static int ans=10000000;
static int n;
//static int num=0;
//static int sum;
static Integer[] cat=new Integer[19];
//static int book[]=new int [18];
//static int flag=0;
static int cab[]=new int [100];
//static int cat[]=new int [100];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
w=sc.nextInt();
for(int i=1;i<=n;i++)
{
cat[i]=sc.nextInt();
}
//排个序,先放大的,减少搜索次数
Arrays.sort(cat,1,n+1,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2-o1;
}
}) ;
dfs(1,0);
System.out.println(ans);
sc.close();
}//应该排个序,先排小的,最后排大的
//cur现在小猫的个数
//cnt 现在车的数目
//版本2 枚举情况最后求min
static void dfs(int cur,int cnt)
{
if(cnt>=ans) return ;//剪枝
if(cur==n+1) {ans=Math.min(cnt,ans);return;}//达到递归边界,就更新
//每拿到一直小猫,先往已有的cnt量车里比对,装不下就新开一辆车
for(int i=1;i<=cnt;i++)
{
if(cab[i]+cat[cur]>w) continue;//剪枝,如果超重就跳过这个小猫
cab[i]+=cat[cur];//把当前小猫放到车里
dfs(cur+1,cnt);//这个地方容易越界?
cab[i]-=cat[cur];//回溯
}
//能来到这里,说明前面没有找到小猫的位置
cab[cnt+1]=cat[cur];
dfs(cur+1,cnt+1);
cab[cnt+1]=0;//回溯
}
}