动态规划第二题,嗯嗯这道题已经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;
}