车的放置-蓝前杯算法训练

持续更新蓝桥杯算法训练题解,有兴趣可以关注一波呀。

车的放置

题目类似于八皇后问题,但是也有不同。由于是放置的棋子是车,所以只要求棋子不在同一行同一列即可,不需要对角线。另外没有规定棋子的个数,所以需要枚举放置棋子的个数是多少,然后转换成求八皇后的问题。

题目中的数据量很小,可以直接采用暴力的方法,时间复杂度 O ( n n ) O(n^n) O(nn)

暴力的思路是,对于每一行,选择没有棋子的一列放置一枚棋子,或者不放棋子。

这里有采用了两种方式:

  • 按照题目描述,枚举放置1,2…,n个棋子进行求解。
  • 直接遍历棋盘,如果放置了一个棋子,就直接把答案加1。

但是还有时间复杂度更低的做法,就是dp。时间复杂度 O ( n 4 ) O(n^4) O(n4),可以解决100以内的n。

时间复杂度的证明如下:

1 ∗ 1 ∗ n + 2 ∗ 2 ∗ n + 3 ∗ 3 ∗ n + 4 ∗ 4 + . . . n 3 = ( 1 2 + 2 2 + 3 2 + . . . n 2 ) ∗ n = n ∑ k = 1 n k 2 1 * 1 * n + 2 * 2 * n + 3 * 3 * n + 4 * 4 + ... n^ 3 = (1^2 + 2^2 + 3^2 +...n^2) * n = n\sum_{k=1}^nk^2 11n+22n+33n+44+...n3=(12+22+32+...n2)n=nk=1nk2~ n 4 n^4 n4

可以使用dp的原因是,可以发现,对于每一行来说,有几个选择,只和前面放置了多少个车以及总共有多少个车有关。比如总共有5个车,前面行放置了2个车,那么现在的行就有3个列可以选择。由于车是相同的,所以只需要计算车放在随便一列的个数,然后乘以3就是当行的答案了。

暴力方法0:

  • 最初的暴力方法,枚举每一个位置,而不是枚举每一行,可以发现完全没有必要枚举每一行,因为只能向下走。而不放已经代表了这一种情况。
 public static int dfs (int[] l, int cur, int curh) {
        if (cur == m) {
            return 1;
        }
        int ans = 0;
        for (int i = curh; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (l[j] == 0) {
                    l[j] = 1;
                    ans += dfs(l, cur + 1, i + 1);
                    l[j] = 0;
                }
            }
        }
        return ans;
    }

暴力方法1:

 public static int dfs (int[] l, int cur, int curh) {		
     // l[i]判断第i列是否放置了棋子, cur已经放置的棋子个数,curh当前的行数
     if (cur == m) {
         return 1;
     }
     int ans = 0;
     for (int j = 0; j < n; j++) {
         if (l[j] == 0) {
             l[j] = 1;
             ans += dfs(l, cur + 1, curh,  + 1);
             l[j] = 0;
         }
     }
     return ans;
 }

暴力方法2:

private static int res = 1;
public static void dfs2(int curl, int[] visc) {
    if (curl == n) return ;
    // 在curh + 1行上面,从这些行中选出一个可以放的放上
    for (int j = 0; j < n; j ++) {
        if (visc[j] == 0) {
            visc[j] = 1;
            // 放上,答案就会多一个
            res ++;
            dfs2(curl + 1, visc);
            visc[j] = 0;
        }
    }
    // 不放
    dfs2(curl + 1, visc);
}

dp方法:

private static int[][][]dp;
public static int dfs(int curh, int cur) {
    if (cur == m) return 1;
    if (curh >= n) return 0;

    int tmp = dp[m][curh][cur];
    if (tmp >= 0) return tmp;
    tmp = 0;
    // 啥都不放
    tmp += dfs(curh + 1, cur);     
    // 总共m个,前面放了cur个,还有(n - cur)个位置可以放
    tmp += dfs(curh + 1, cur + 1) * (n- cur);
    dp[m][curh][cur] = tmp;
    return tmp;
}

AC代码:

package lanqiao.imporve;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @author: Zekun Fu
 * @date: 2022/10/13 9:59
 * @Description: 车的放置
 *
 * 八皇后问题简化版:不能在同一行,同一列中
 */
public class CheDeFangZhi {

    private static int n;
    private static int m;
    // 为了放置重复,每一个都从下一行开始放置
    public static int dfs (int[] l, int cur, int curh) {
        if (cur == m) {
            return 1;
        }
        int ans = 0;
        for (int i = curh; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (l[j] == 0) {
                    l[j] = 1;
                    ans += dfs(l, cur + 1, i + 1);
                    l[j] = 0;
                }
            }
        }
        return ans;
    }
    public static int[][][]dp;
    public static int dfs(int curh, int cur) {
        if (cur == m) return 1;
        if (curh >= n) return 0;

        int tmp = dp[m][curh][cur];
        if (tmp >= 0) return tmp;
        tmp = 0;
        // 啥都不放
        tmp += dfs(curh + 1, cur);      // 啥都不放
        // 总共m个,前面放了cur个,还有(n - cur)个位置可以放
        tmp += dfs(curh + 1, cur + 1) * (n- cur);
        dp[m][curh][cur] = tmp;
        return tmp;
    }
    private static int res = 1;
    public static void dfs2(int curl, int[] visc) {
        if (curl == n) return ;
        // 在curh + 1行上面,从这些行中选出一个可以放的放上
        for (int j = 0; j < n; j ++) {
            if (visc[j] == 0) {
                visc[j] = 1;
                // 放上,答案就会多一个
                res ++;
                dfs2(curl + 1, visc);
                visc[j] = 0;
            }
        }
        // 不放
        dfs2(curl + 1, visc);
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        // 方法1:
//        int ans = 0;
//        for (int i = 0; i <= n; i++) {
//            int[] l = new int[n];
//            m = i;
//            ans += dfs(l, 0, 0);
//        }
        // 方法2
//        int[]vis = new int[n];
//        dfs2(0, vis);
//        System.out.println(res);
       // dp
        int ans = 0;
        dp = new int[(n + 1)][(n + 1)][(n + 1)];
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= n; j++) {
                Arrays.fill(dp[i][j], -1);
            }
        }
        for (int i = 0; i <= n; i++) {
            m = i;
            ans += dfs(0, 0);
        }
        System.out.println(ans);

    }
}

/*
*
* dp[n][i] = dp[n - 1][i - 1] * n (i >= 2)
*
*   1
*   3 * 3 = 9
*   3 * 4 = 12 + 3 * 2 = 18
*   3 * 2 = 6
* 10 + 18 = 28 + 6 = 34
*
* 也就是说少了dp[n][m][curh] 这种情况
*
* 递归正确,递推出错。md
*
* */

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值