【編程題】中國象棋&路燈

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);
    }
}

 

 

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