棋盘分割 错误账目(递归问题 枚举 回溯)

 棋盘分割   
 

将一个8*8的棋盘进行如下分割: 将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行) 

 
                                       图 9-5 棋盘分割示意图
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成 n 块矩形棋盘,并使各矩形棋盘总分的均方差最小。  均方差 ,其中平均值 ,xi 为第i 块矩形棋盘的总分。  请编程对给出的棋盘及 n,求出 O'的最小值。 

解题思路:

给一个矩形框可以横着分割,竖着分割,枚举横着和竖着分割的所有情况,每次横着分割右两种情况,保存上半段,继续分割下半段,保存下半段,继续分割上半段,每次竖着分割也有两种情况,保存左半端,继续分割右半段,保存右半段,继续分割左半段。

Input

第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。

Output

仅一个数,为O'(四舍五入精确到小数点后三位)。

Sample Input

3
1 1 1 1 1 1 1 3
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0
1 1 1 1 1 1 0 3

Sample Output

1.633


import java.text.DecimalFormat;
import java.util.Scanner;

public class LX9_8 {

    private static int[][] a=new int[8][8];//棋盘每个方格的分值
    private static int n;//需要分割的矩形的个数
    private static int[] size;//保存n个矩形的面积
    private static double result=10;//保存最小的均方差
    private static int total=0;//8*8棋盘分值的总和
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        size=new int[n];
        
        //得到棋盘每个方格的分值
        for(int i=0;i<8;i++)
        {
            for(int j=0;j<8;j++)
            {
                a[i][j]=sc.nextInt();
                total+=a[i][j];
            }
        }
        f(0,0,0,7,7);
        DecimalFormat df = new DecimalFormat("0.000");
        System.out.println(df.format(result));
        
    }
    /*第i次分割的所有情况  startx starty要分割的矩形棋盘起始位置的行和列 endx endy结束位置的行和列*/
    public static void f(int i,int startx,int starty,int endx,int endy)
    {
        if(startx==endx&&endx==endy)//矩形的起始位置和终止位置是同一个位置,不能再分了,也就是说这种情况不能分成n个矩形
            return;
        if(i==n-2)//最后一次分割  两部分的分值之和都要保存
        {
            //横着分割的所有情况 j代表在该行下分割
            for(int j=startx;j<=(endx-startx)/2+startx;j++)
            {
                //保存两部分
                size[i]=jiSuan(startx,starty,j,endy);
                size[i+1]=jiSuan(j+1,starty,endx,endy);
                junFangCha();
            }
            //竖着分割的所有情况
            for(int j=starty;j<=(endy-starty)/2+starty;j++)
            {
                //保存两部分
                size[i]=jiSuan(startx,starty,endx,j);
                size[i+1]=jiSuan(startx,j+1,endx,endy);
                junFangCha();
            }
        }
        else
        {
            //横着分割的所有情况 j代表在该行下分割
            for(int j=startx;j<=(endx-startx)/2+startx;j++)
            {
                //保存上半部分,继续分割下半部分
                size[i]=jiSuan(startx,starty,j,endy);
                f(i+1,j+1,starty,endx,endy);
                //保存下半部分,继续分割上半部分
                size[i]=jiSuan(j+1,starty,endx,endy);;
                f(i+1,startx,starty,j,endy);
            }
            //竖着分割的所有情况
            for(int j=starty;j<=(endy-starty)/2+starty;j++)
            {
                //保存左半部分,继续分割右部分
                size[i]=jiSuan(startx,starty,endx,j);
                f(i+1,startx,j+1,endx,endy);
                //保存右半部分,继续分割左部分
                size[i+1]=jiSuan(startx,j+1,endx,endy);
                f(i+1,startx,starty,endx,j);
            }
        }
    }
    //给出一个起始位置和结束位置的矩形,求出该矩形的分值和
    public static int jiSuan(int startx,int starty,int endx,int endy)
    {
        int sum=0;
        for(int i=startx;i<=endx;i++)
        {
            for(int j=starty;j<=endy;j++)
            {
                sum+=a[i][j];
            }
        }
        return sum;
    }
    //给出n个矩形的分值和求出均方差
    public static void junFangCha()
    {
        //求平均值
        double m=total/(float)n;
        double sum=0;
        for(int i=0;i<n;i++)
        {
            double k=Math.pow(size[i]-m, 2);
            sum+=k;
        }
        double j=Math.sqrt(sum/n);
        if(j<result)
            result=j;
    }

}

问题:上传到poj上时说运行错误?

错误账目:

某财务部门结账时发现总金额不对头。很可能是从明细上漏掉了某1笔或几笔。如果已知明细账目清单,能通过编程找到漏掉的是哪1笔或几笔吗?

如果有多种可能,则输出所有可能的情况。

我们规定:用户输入的第一行是:有错的总金额。

接下来是一个整数n,表示下面将要输入的明细账目的条数。

再接下来是n行整数,分别表示每笔账目的金额。

要求程序输出:所有可能漏掉的金额组合。每个情况1行。金额按照从小到大排列,中间用空格分开。

比如:

用户输入:

6

5

3

2

4

3

1

表明:有错的总金额是6;明细共有5笔。

此时,程序应该输出:

1 3 3

1 2 4

3 4

为了方便,不妨假设所有的金额都是整数;每笔金额不超过1000,金额的明细条数不超过100

思路:取n个数,然后尝试所以组合是否满足条件,满足则输出,不满足则继续尝试(类似碎纸机   poj书上的练习题)

import java.util.Arrays;
import java.util.Scanner;

/*错误账目*/
public class WrongAccount {

    private static int total;//错误的金额总数
    private static int  n;//账目笔数
    private static int[] a;//n笔账目的金额
    private static boolean[] flag;//标记账目是否在其中
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Scanner sc=new Scanner(System.in);
        total=sc.nextInt();
        n=sc.nextInt();
        a=new int[n];
        flag=new boolean[n];
        for(int i=0;i<n;i++)
        {
            a[i]=sc.nextInt();
        }
        //数组a排序从小到大
        Arrays.sort(a);
        for(int i=1;i<=n;i++)
        {
            f(i,0,0);
        }
    }
    //num选几个数,cur当前选的第几个数,start当前数可以从该数开始选取
    public static void f(int num,int cur,int start)
    {
        if(cur==num)//num个数选完成
        {
            jiSuan();
            return;
        }
        if(start>=n)//没有选够数,但是已经没有数可以选
        {
            return;
        }
        for(int i=start;i<n;i++)
        {
            flag[i]=true;
            f(num,cur+1,i+1);
            flag[i]=false;
        }
        
    }
    public static void jiSuan()
    {
        int sum=0;
        for(int i=0;i<n;i++)
        {
            if(flag[i])//使用
            {
                sum+=a[i];
            }
        }
        //结果等于目标数
        if(sum==total)
        {
            for(int i=0;i<n;i++)
            {
                if(flag[i]==false)//没有使用
                {
                    System.out.print(a[i]+" ");
                }
            }
            System.out.println();
        }
    }

}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值