诸侯安置 简单的递推

诸侯安置

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;
}

猜你喜欢

转载自blog.csdn.net/CutieDeng/article/details/82860338