每日刷題:lightoj 1005 - Rooks

動態規劃第二題,嗯嗯這道題已經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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章