【编程题】中国象棋&路灯

1.中国象棋

题目描述:在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法。在中国象棋中炮的行走方式是:一个炮攻击到另一个炮,当且仅当它们在同一行或同一列中,且它们之间恰好有一个棋子。

输入:

一行包含两个整数N,M,之间由一个空格隔开。

输出:

总共的方案数,由于该值可能很大,只需给出方案数mod 9999973的结果。

样例输入:

1 3

样例输出:

7

说明/提示

样例说明

除了3个格子里都塞满了炮以外,其它方案都是可行的,所以一共有2*2*2-1=7种方案。

数据范围

100%的数据中N和M均不超过100

50%的数据中N和M至少有一个数不超过8

30%的数据中N和M均不超过6

分析过程:

首先考虑合法的状态,即每一行每一列的炮的数量≤2,所以考虑用一个数组存储有几列放了一个炮,有几列放了两个炮。因此定义状态数组f[i][j][k]代表在前i行,有j列放了1个棋子,有k列放了2个棋子的合法方案数。这个时候知道了全部的列数,以及知道部分情况的列数,因此可以求出不放棋子的列数,即空的=全部-合法的,也就是空的列数=m-j-k。因此我们可以确定:

  1. 可以在当前第i行不放棋子
  2. 可以在当前第i行放1个棋子
  3. 可以在当前第i行放2个棋子

然后分析上述三种情况:

  • 不放棋子,则直接继承上面的状态,也就是f[i][j][k]=f[i-1][j][k]
  • 放1个棋子:显然我们不会选择将棋子放在已经有两个棋子的列上。因此存在两种情况:
    • 1. 将棋子放在原来只有1个棋子的列上:f[i][j][k]+=f[i-1][j+1][k-1]\times (j+1)
    • 2. 将棋子放在原来没有棋子的列上:f[i][j][k]+=f[i-1][j-1][k]\times (m-(j-1)-k)

这里需要解释这两种情况:

1. 放在原来只有1个棋子的列:我们在某一个有一个棋子的列放置棋子,会使这一列变成有两个棋子,即要得到f[i][j][k]需要在j+1个有一个棋子的列上放置棋子,然后变成j个有一个棋子的列,同时又会得到一个新的有两个棋子的列,因此在此之前必须有k-1个放置2个棋子的列。所以f[i-1][j+1][k-1]的状态传递给f[i][j][k]存在(j+1)种情况,即可以在(j+1)列中的任意一列放置1个棋子。

2. 放在原来没有棋子的列:我们在一个没有棋子的列放置棋子,会得到一个新的有1个棋子的列。即要从j-1得到j,而这个过程中放置有2个棋子得列的数量是不会变的,所以从k传递即可,即f[i-1][j-1][k]的状态可以传递给f[i][j][k]。因为存在有(m-(j-1)-k)个空列,所以要乘以(m-(j-1)-k)

  • 放2个棋子:这个时候存在3种放法:
    • 1. 将其中1个棋子放在原来只有1个棋子的列,另一个棋子放在原来没有棋子的列:f[i][j][k]+=f[i-1][j][k-1]\times j\times (m-j-(k-1))
    • 2. 都放在原来没有棋子的列:f[i][j][k]+=f[i-1][j-2][k]\times C_{m-(j-2)-k}^{2}
    • 3. 都放在原来只有1个棋子的列:f[i][j][k]+=f[i-1][j+2][k-2]\times C_{j+2}^{2}

这里需要解释这三种情况:

1. 将其中1个棋子放在原来存在1个棋子的列,另一个放在原来不存在棋子的列。这个时候,我们放置之后,状态就变成了:一个没有棋子的列会变成一个存有1个棋子的列,另一个原本存有1个棋子的列变成了存有2个棋子的列。此时我们发现存在1个棋子的列的数量不会变,即第二维依然是j,又因为我们新增了一个存有2个棋子的列,所以需要从k-1转移过来,同时又因为我们可以在原来存有1个棋子的列中任意选择,而且空的列也可以任意放,所以需要\times j\times (m-j-(k-1))

2. 都放在原来没有棋子的列。放置之后,会新增两个存有1个棋子的列,因此需要从j-2转移过来,而原来存有2个棋子的列的数量不变,仍然是k个。同时,因为在空的列中可以任意放置,根据排列组合公式,有C_{m-(j-2)-k}^{2}种情况。

3. 都放在原来存有1个棋子的列。放置之后,这两个原本存有1个棋子的列就变成了两个存有2个棋子的列。即从j+2变成了j,从k-2变成了k。而且这两个棋子可以在任意原本存有1个棋子的列中任意选择,根据排列组合公式,有C_{j+2}^{2}种情况。

最后需要注意的就是在遍历过程中的边界判断。

算法实现:

import java.util.Scanner;

public class Chess {
    public static int f[][][] = new int[101][101][101];
    public static int MOD = 9999973;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt(); // 行数
        int M = sc.nextInt(); // 列数
        int res = getResult(N, M);
        System.out.println(res);
    }

    private static int getResult(int n, int m) {
        f[0][0][0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) {
                for (int k = 0; j + k <= m; k++) {
                    f[i][j][k] = f[i - 1][j][k];
                    if (k >= 1) {
                        f[i][j][k] += f[i - 1][j + 1][k - 1] * (j + 1);
                    }
                    if (j >= 1) {
                        f[i][j][k] += f[i - 1][j - 1][k] * (m - j - k + 1);
                    }
                    if (k >= 2) {
                        f[i][j][k] += f[i - 1][j + 2][k - 2] * MathC(j + 2);
                    }
                    if (k >= 1) {
                        f[i][j][k] += f[i - 1][j][k - 1] * j * (m - j - k + 1);
                    }
                    if (j >= 2) {
                        f[i][j][k] += f[i - 1][j - 2][k] * MathC(m - j - k + 2);
                    }
                    f[i][j][k] %= MOD;
                }
            }
        }
        int res = 0;
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= m; j++) {
                res += f[n][i][j];
            }
        }
        return res;
    }

    private static int MathC(int i) {
        return ((i * (i - 1)) / 2) % MOD;
    }
}

2.路灯

题目描述:妞妞有一天工作到很晚,回家的时候要穿过一条长l的笔直的街道,这条街道上有n个路灯。假设这条街起点为0,终点为l,第i个路灯座标为ai。路灯发光能力以正数d来衡量,其中d表示路灯能够照亮的街道上的点与路灯的最远距离,所有路灯发光能力相同。为了让妞妞看清回家的路,路灯必须照亮整条街道,又为了节省电力希望找到最小的d是多少?

输入:

输入两行数据,第一行是两个整数:路灯数目n (1≤n≤1000),街道长度l (1 ≤l≤10^9)。第二行有n个整数ai (0 ≤ ai≤ l),表示路灯座标,多个路灯可以在同一个点,也可以安放在终点位置。

输出:

输出能够照亮整个街道的最小d,保留两位小数。

样例输入:

7 15

15 5 3 7 9 14 0

样例输出:

2.50

分析过程:

找出相邻座标差值中最大的,然后除以2就是最小的d。具体步骤如下:

  1. 输入路灯数目n、街道长度l;
  2. 输入路灯位置ai;
  3. 计算相邻路灯之间的距离S1,S2...
  4. 由于路灯发光能力相同,按照相邻路灯灯光刚好连接,最短发光距离应该是d=Si/2;
  5. 由于发光能力相同,而Si各不相同,为了满足照亮整个街道(灯光至少刚好连接)的条件,因此选取最大的Si*0.50;

算法实现:

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

public class StreetLamp {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String n_l = sc.nextLine();
        String[] strNL = n_l.split(" ");
        int n = Integer.parseInt(strNL[0]);
        int l = Integer.parseInt(strNL[1]);
        if ((n >= 1 && n <= 1000) && (l >= 1 && l <= Math.pow(10, 9))) {
            String temp = sc.nextLine();
            if (!temp.isEmpty()) {
                String[] strNum = temp.split(" ");
                int len = strNum.length;
                if (len == n) {
                    int[] ai = new int[len];
                    for (int i = 0; i < len; i++) {
                        int aiTemp = Integer.parseInt(strNum[i]);
                        if (aiTemp < 0 || aiTemp > l) {
                            break;
                        }
                        ai[i] = aiTemp;
                    }
                    String res = getResult(ai, n, l);
                    System.out.println(res);
                }
            }
        }
    }

    private static String getResult(int[] ai, int n, int l) {
        Arrays.sort(ai);
        for (int i = 0; i < n; i++) {
            System.out.println(ai[i]);
        }
        double res = 0.00;
        int i = 1;
        while (i <= n - 1) {
            double x = (ai[i] - ai[i - 1]) * 0.50;
            if (x > res) {
                res = x;
            }
            i++;
        }
        if (ai[0] - 0 > res) {
            res = ai[0] - 0;
        }
        if (l - ai[n - 1] > res) {
            res = l - ai[n - 1];
        }
        return String.format("%.2f", res);
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章