UVa10237 Bishops

文章讲述了如何解决在n×n棋盘上放置k个互不攻击的象的问题。通过将棋盘分为两类并转换为阶梯形状,定义了一个多维递推状态f[h1][c1][h2][c2][k],描述了不同高度列的象放置计数。通过状态转移公式进行计算,并提供了AC代码示例,主要思路是递推算法和排列组合优化。
摘要由CSDN通过智能技术生成

题目链接

        UVa10237 Bishops

题意

        两个象互不攻击,当且仅当它们不处于同一条斜线上。输入整数n(n≤30),统计在一个n×n 的棋盘上放k 个互不攻击的象有多少种方法。如n=8,k=6 时有5 599 888 种。

        

分析

        棋盘上能相互攻击的方格可以分成两类,两类之间任意方格上的象无法相互攻击,可以独立考虑。

        还可以看出,n为偶数时两分类相同,n为奇数时两分类不相同。每个分类所含的方格如果放象,则可能相互攻击,把每个分类对应的图旋转45度变正,则每条水平/竖直线上最多有一个格子放象。

        任意列可以交换顺序,计数的结果不变,因此可以将每种分类按列的长度从大到小重排,重新排后,任意行也可以交换顺序,最后可以变成阶梯形状。

        经过这样的一个脑洞过程,可以定义一个多维递推状态:f[h1][c1][h2][c2][k](f[N][2][N][2][N]),表示阶梯最大高度为h1,最大高度的列有c1+1个(c1<2),最小高度为h2,最小高度的列有c2个(c1<2)放k各象时部相互攻击的摆放方法计数。

        状态转移就很清晰了:高度最小的列上可以不放象,计数为f[h1][c1][h2][c2-1][k];高度最小的列上放一个象,计数为和h2*f[h1-1][c1][h2-1][c2-1][k]。

        递推式为f[h1][c1][h2][c2][k] = f[h1][c1][h2][c2-1][k] + h2*f[h1-1][c1][h2-1][c2-1][k]。

        考虑一些实际的边界情况,状态转移会再复杂一点,但主体思路就是这样,时间复杂度为

        很烧脑的一道题目,本人很享受脑洞出多维递推状态的过程,更多细节参见AC代码。

AC代码


#include <iostream>
using namespace std;

#define N 31
long long f[N][2][N][2][N] = {0}; int n, k;

long long solve() {
    if (n == 1) return k<=1;
    if (k > 2*n-2) return 0;
    if (n & 1) {
        long long (&r1)[N] = f[n][0][1][1], (&r2)[N] = f[n-1][1][n>3 ? 2 : 0][1], ans = 0;
        for (int i=0; i<=k && i<n; ++i) ans += r1[i]*r2[k-i];
        return ans;
    }
    long long (&r)[N] = f[n][0][n>2 ? 2 : 0][1], ans = 0;
    for (int i=0; i<=k && i<n; ++i) ans += r[i]*r[k-i];
    return ans;
}

int main() {
    for (int h1=2; h1<N; ++h1) for (int c1=0; c1<2; ++c1) {
        f[h1][c1][0][0][0] = f[h1][c1][0][1][0] = 1;
        f[h1][c1][0][0][1] = f[h1][c1][0][1][1] = (c1+1)*h1;
        if (c1) f[h1][c1][0][0][2] = f[h1][c1][0][1][2] = h1*(h1-1);
        for (int h2 = h1-2; h2>0; h2 -= 2) for (int c2=0; c2<2; ++c2) {
            f[h1][c1][h2][c2][0] = 1;
            for (int k=1, m=min(h1, h1-h2+c1+c2); k<=m; ++k) {
                f[h1][c1][h2][c2][k] = f[h1][c1][c2 ? h2 : (h2+2<h1 ? h2+2 : 0)][!c2][k]
                    + h2 * f[h1-1][c1][c2 ? (h2>1 ? h2-1 : (h1>3 ? 2 : 0)) : (h2+2<h1 ? h2+1 : 0)][h2>1 ? !c2 : 1][k-1];
            }
        }
    }
    while (cin>>n>>k && n) cout << solve() << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值