【洛谷】 P1240 诸侯安置(递推) —— 酱懵静

洛谷P1240 诸侯安置

点击此处去OJ
问题描述
很久以前,有一个强大的帝国,它的国土成正方形状(需旋转45°来看),如图所示。 这个国家有若干诸侯。由于这些诸侯都曾立下赫赫战功,国王准备给他们每人一块封地(正方形中的一格)。但是,这些诸侯又非常好战,当两个诸侯位于同一行或同一列时,他们就会开战。如下图2—3为n=3时的国土,阴影部分表示诸侯所处的位置。前两幅图中的诸侯可以互相攻击,第三幅则不可以。

在这里插入图片描述
国王自然不愿意看到他的诸侯们互相开战,致使国家动荡不安。 因此,他希望通过合理的安排诸侯所处的位置,使他们两两之间都不能攻击。
现在,给出正方形的边长n,以及需要封地的诸侯数量k,要求你求出所有可能的安置方案数。(n≤l00,k≤2n-2)
由于方案数可能很多,你只需要输出方案数除以504的余数即可。
(注意:镜面和旋转的情况属于不同的方案)

输入格式:仅一行,两个整数n和k,中间用一空格隔开。
输出格式:一个整数,表示方案数除以504的余数。

输入样例:2 2
输出样例:4



---分割线---



分析:
我第一次看时没搞懂这所谓的正方形是怎么的(因为怎么看都觉得不像正方形,这里是真的有点坑,麻烦你们绘图标准点可好?),相信也有同学和我犯了一样的错
下面给出部分n递增的图案,方便理解题意:
在这里插入图片描述

接下来开始正式分析这道题:
其实它的意思和八皇后挺相似的(简言之:不能同行或同列),不过简单很多。首先有一步很考智商的操作(别问为什么,问就是不知道):将整行/列平移,其并不影响诸侯间的限制关系,自己想为什么。如下:
在这里插入图片描述



这个变换可谓相当的关键,也是整道题的解题思路之灵魂所在,根据这个图我们来找规律:
先假设n=5,k=5,即5个诸侯被安置
我们看右边变换后的图,首先,在最左边的哪一列格子(也仅一个格子)安置第一个诸侯,如下:
在这里插入图片描述


然后,在上一个被安置了的诸侯的哪一行的上一行的最左边安置第二个诸侯
再然后,重复上一步的操作安置第三个诸侯、第四个诸侯,最后的结果如下:
在这里插入图片描述



剩下最后一个诸侯,我们发现,对于最后一列,其有(2n-1)-(k-1)=5个放置位置,如下:
备注:(2
n-1)表示最后一列的长度

在这里插入图片描述



推广来看:假设n不变,k=6或者k=4时,这样的放置方案数量均可用上面的公式得到
例:当k>5,比如k=6时,我们首先让前面4个诸侯的放置方式与k=5时的放置方式一致,但是第5个诸侯则将其放置在第四列的最后一排,如下:
在这里插入图片描述



这时,对于最后一个诸侯而言,其能放置的位置也就是剩下的9-5=4个位置了,如下:
在这里插入图片描述
带入上面的那个公式,发现其仍然成立(2*5-1)-(6-1)==5
对于k<5时同样如此。
这时候,我们便能在其中寻找到一些递推公式的线索了。

根据上面的分析,我们假设前面k-1个诸侯有f个放置方案
则在这样的前提下(假设前面的放置方式固定不变),若在最后一列放置最后一个诸侯,共会有f*[(2*n-1)-(k-1)]种放置方案
而显然,最后一列放置诸侯只有两种情况:要么不放、要么放置一个(则仅能放一个)
所以,总的安置诸侯的方案数,应该等于上述两种情况之和
于是设f[i][j]表示前i列里安置了j个诸侯时的放置方案数,则有:
f [ i ][ j ]=f [ i-1 ][ j ]+f [ i-1 ][ j-1 ] ╳ ( len(i) - (k-1) )

① f[i-1][j]表示前面i-1列安置完了所有的诸侯时的方案数
② f[i-1][j-1]*(len(i)-(k-1))表示前i-1列安置j-1个诸侯,最后一列安置最后一个诸侯时的方案数
其中,len(i)表示第i列的长度

这时候我们再返回来仔细思考这种假设是否合理(即:这种变换方式能不能保证枚举的完整性和独立性?)
从公式入手,不难看出数据的递推是从最左边的那一列开始的(i=1,j=1),而后再逐渐地往后面拓展。假设你的数据不足以安置所有的诸侯,那么这种前提下即为0(注意:这样的0是指没有任何一种方案能得以使其不为0)这样的现状会一直维持到国土面积足以安置下当前数量的诸侯,这时候,此公式的递推开始出现了非零值。而当这样的第一个非零值出现时,其只会有上述的两种情况之一。显然,情况②的出现是依赖于情况①的(换言之,情况①会优先出现非0值),即情况②的放置情况是在情况①所构成的结果集合中,产生的笛卡儿积。这样,就保证了在接下来枚举的情况中不会出现重复枚举,并且能够把所有结果都枚举到。同理,对于下一个情况的方案也是在其前一次情况唯一的前提下所确定的。这样,就保证了对于所有枚举的情况的唯一性以及对所有需要枚举的结果的不缺漏。而对于接下来的结果,其只能是:
1.一个唯一情况
2. 一个多结果,但每个结果彼此唯一
最终的结果便由上述两种情况不断递推而出,于是往后,就可以一直迭代下去,并且在这过程中保证方案的不重合性和各个方案间的独立性。
换言之就把上述的公式推广到了任意前提下。
至此,便完成了对诸侯安置数量的统计,下面给出本题的代码:


---分割线---


#include<iostream>
using namespace std;

int f[205][205];	//该函数的两个参数分别代表列数i、诸侯数j,整个函数f[i][j]表示前i列分给j个诸侯的划分方案数 
int len[205];		//表示某列i的长度 

int main()
{
	int n,k,i,j;
	cin>>n>>k;
	if(k>=2*n-1)
	{
		cout<<0<<endl;
		return 0;
	}	
	//设置每一列的长度 
	for(i=1;i<n;i++)
		len[2*i-1]=len[2*i]=2*i-1;
	len[2*n-1]=2*n-1;
	//给每一个f的第一个值赋初值1 
	for(i=0;i<=2*n-1;i++)
		f[i][0]=1; 
	//转移方程(递推公式) 
	for(i=1;i<=2*n-1;i++)
		for(j=1;j<=len[i];j++)
		{
			f[i][j]=f[i-1][j]+f[i-1][j-1]*(len[i]-(j-1));
			f[i][j]%=504;
		}
	cout<<f[2*n-1][k]<<endl;
	return 0;
 }

发布了30 篇原创文章 · 获赞 67 · 访问量 3057

猜你喜欢

转载自blog.csdn.net/the_ZED/article/details/100108584
今日推荐