1. 程式人生 > >諸侯安置 簡單的遞推

諸侯安置 簡單的遞推

諸侯安置

Problem Description
很久以前,有一個強大的帝國,它的國土呈正方形狀(轉45度看),如圖所示。
n=3時的國土
這個國家有若干諸侯。由於這些諸侯都曾立下赫赫戰功,國王準備給他們每人一塊封地(正方形中的一格)。但是,這些諸侯又非常好戰,當兩個諸侯位於同一行或同一列時,他們就會開戰。如下圖為n=3時的國土,陰影部分表示諸侯所處的位置。前兩幅圖中的諸侯可以互相攻擊,第三幅則不可以。第一幅、第二幅圖諸侯會開戰,第三幅圖不會開戰
國王自然不願意看到他的諸侯們互相開戰,致使國家動盪不安。因此,他希望通過合理的安排諸侯所處的位置,使他們兩兩之間都不能攻擊。
現在,給出正方形的邊長n,以及需要封地的諸侯數量k,要求你求出所有可能的安置方案數。(n≤100,k≤2n2-2n+1)
由於方案數可能很多,你只需要輸出方案數除以504的餘數即可。
Input
僅一行,兩個整數n和k,中間用一空格隔開。
Output
一個整數,表示方案數除以504的餘數。
Sample Input
2 2
Sample Output
4
Tip
四種安置方案如圖所示。注意:鏡面和旋轉的情況屬於不同的方案。這四種情況屬於不同方案

觀察本題,很顯然,我們會發現本題類似於八皇后問題。
當然,這道題跟八皇后問題的很不相同。因為,如果我們從左向右觀察,我們會發現,它的寬度從1向2n-1,以2為公差遞增,呈等差數列,而後對稱。
因此,它的寬度變化是先單調遞增,而後單調遞減的。
如果我們只看一半,我們可以發現,如果對於前i列,它們的寬度為2i-1,那麼如果我們已知這樣一個函式f[i][j],表示前i列有j個諸侯的可能數,通過之前的規律我們知道,新的一列i+1,諸侯可以出現的地方有2i-1-j。
因此,就有遞推方程f[i][j]=f[i-1][j-1]*(2i-1-j)
但是,如果前面的這一列,也就是 i-1 列沒有諸侯,我們怎麼處理呢,那麼,我們就可以得到一個讓之前的 i-1 變化一下,變成 f[i][j]=∑f[i-k][j-1] (1≤k≤i-1)
這樣,我們就可以明確f[i][j]的含義,是在第i列存在諸侯,前i列有j個諸侯的可能數。
這樣,我們就可以完整的記錄所有的情況了。
因為f[i][j] 與 f[i-1][j]它們所包含的情況必然是不存在交集的。
所以,我們的思路可以朝著這個方向進行。
但是,問題在於,題目描述中,領土是先遞增,後減少,不能夠用這個函式來完整描述。
不過,我們想一想。對於一個諸侯來說,與它在同一行不能存在其他諸侯,與它在同一列不能存在其他諸侯。
但是,如果我們將這一列與另一列調換,保證不生成新的行(也就是說,調換後所有各自仍然連在一起,不能因為列的改變導致一行出現中間隔斷的情況)最簡單的調換方法就是,將列的寬度進行排序,這樣就可以直接得到一個寬度遞增的一個圖形。
如圖所示:這是對調了列的情況


並且,我們可以知道,對於這個圖形來說,它相同寬度的列只有兩個(除最大寬度列),那麼,我們就可以通過剛才推到的遞推式,再根據它的限制,列的寬度限制,進行遞推,就可以得到答案了。
程式碼如下。

#include <cstdio>
#include <iostream>
 
using namespace std;
 
int f[210][2000];
int n,k,ans,p;
 
int main()
{
    scanf("%d%d",&n,&k);
    f[0][0]=1;
    if (k+1<=2*n)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=((i<n)?2:1);j++)
            {
                ++p;
                for (int c=1;c<=min(p,k);c++)
                    for (int r=0;r<p;r++)
                        (f[p][c]+=f[r][c-1]*(i*2-c))%=504;
            }
        for (int i=0;i<=p;i++)
            ans+=f[i][k];
        ans%=504;
    }
    printf("%d",ans);
    return 0;
}