每日刷题:lightoj 1005 - Rooks

本文介绍了一道关于在N×N棋盘上放置K个棋子的问题,要求棋子间不能处于同一行或列形成攻击态。通过动态规划方法求解不同规模下可行的放置方案数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

动态规划第二题,嗯嗯这道题已经AC有3-4天了,但现在才来写题解,真是万事开头难,事情一摞摞,计划赶不上变化。
题目呢和之前一样就不放出来了,可以OJ找,题意大概就是:N×N(1<=N<=30)的棋盘里面放置K(0<=K<=N^2)个棋子,如果棋子之间任意两个在同一行或者同一列则称这为攻击态,反之为非攻击态。然后问有多少中放置方法使得棋子之间没有出现攻击态。

假设我们有K个棋子,R1,R2,R3,…,Rk。我们可以看出当K>N的时候,放置第K个棋子时,一定会和任意一个棋子形成攻击态。因此放置方法数为0。当K=0时,要注意时符合题意没有形成攻击态的所以放置方法数为1。

这题其实可以用组合数学的思想去做,选取任意k行C(n,k),然后在K×N的矩阵中选取K列做全排列。也就时 C(n,k)A(n,k)ifk<n

当时呢,因为时DP练手嘛,所以我主要讲用DP的方法去做。

定义dp(i,j):i×i的矩阵放置j个棋子的方法数。
由上面红字的分析可以看出
dp(1,0)=dp(1,1)=1
我们假设N=3,K=2。
在3×3中放置一个棋子R’ 的方法有C(9,1)种,然后剩余可以放置的区域组成了一个2×2的矩阵,还可以放置一个棋子。也就是我们需要知道dp(2,1)的答案,才能解决dp(3,2)。我们假设dp(2,1)已经解决了,那么说其实我们刚才放置的第一个棋子R’(在dp(2,1)已经解决的假设上其实是第二个棋子)的编号其实已经是确定的了。而C(9,1)中隐含了我们选择的棋子的编号是任意的条件,因此会出现重复,所以要除2。
最终可以得到
dp(3,2)=(3 * 3 * dp(2,1))/2

其实到这里状态转移方程就出来了。

dp(i,0)= 1;
dp(i,j)=(i * i * dp(i-1,j-1)) / j ; if i >1, j < i

答案就是dp(n,k).


#include <cstdio>
#include <math.h>
#include<memory.h> 
#include <map>
long long solve(int N)
{
    int K=0;
    scanf("%d",&K); 
    if(K>N) return 0;
    long long dp[31][31];//定义dp[i][j]:边长为i的正方形,有j个rooks所能组成的不攻击数量
    memset(dp,0,sizeof(dp));
    //初始值
    dp[1][0] = 1; 
    dp[1][1] = 1;

    for(int i=2;i<=30;i++)
    {
        dp[i][0]=1;
        for(int j=1;j<=i;j++)
        {
             dp[i][j] = (i*i*dp[i-1][j-1])/j;   
        }
    }
    return dp[N][K];
}

int main() {
    long N, caseno = 0, cases;
    scanf("%d", &cases);
    while( cases-- ) {
        scanf("%d",&N);
        printf("Case %d: %lld\n",++caseno,solve(N));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值