【洛谷】 P1240 诸侯安置(递推)

2024-03-30 13:08

本文主要是介绍【洛谷】 P1240 诸侯安置(递推),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

洛谷P1240 诸侯安置

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

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

输入格式
仅一行,两个整数n和k,中间用一空格隔开。

输出格式
一个整数,表示方案数除以504的余数。

样例输入
2 2

样例输出
4




算法分析

首先要弄清楚,在本题中当n取某个值时,其对应的国土情况是怎样的。
图3
不难看出,无论n取何值,国土的行、列个数均为奇数。接下来我们思考如何求解本题。
对于这一类求“***方案数”,并且还需要将结果对某个数字进行取余的题目而言,往往暗示了其最终结果会很大。此时,通常有两种处理办法:搜索、动态规划。
对于本题而言,搜索算法是很不现实的。因为在用搜索的方式去枚举某个国土的具体分配方案时,递归的层次将取决于k的取值。而我们知道,一个递归算法的递归树如果超过了50层,往往会超时。因此在本题k最大会取到2 ╳ 100 - 2 = 198的情况下,搜索算法是很难完成任务的。另一方面,对于搜索算法枚举出的国土分配方案来说,我们还需要去制定“诸侯们是否会打架?”这个规则,这无疑也会带来一些开销。因此求解本题最合适的方案最终落到了DP的头上。
简单解读一下本题的要求,就是“在一个拥有很多小格子的正方形中分配小格子,且要求分配出去的所有小格子彼此之间都不在同一行或者同一列”。由于要求的格子分配方案是基于任意两个格子之间行、列均不相同而得到的,那么在初始情况下,对整个正方形中的格子在行、列上进行平移是不会影响这个值的。

接下来开始正式分析这道题:
其实它的意思和八皇后挺相似的(简言之:不能同行或同列),不过简单很多。首先有一步很考智商的操作(别问为什么,问就是不知道):将整行/列平移,其并不影响诸侯间的限制关系,自己想为什么。如下:
图4
之所以要进行这个变换,是因为在它将更方便我们去分析其背后所藏的动态转移方程,你也可以将所有的格子按行向下(上)平移,最终所得结果都是一致的。
就以上图为例,假设当前n=5,k=5(即需要在n=5的国土中安置5个诸侯),那我们要如何计算总方案数呢?为了能更清晰地阐述这个选取过程,我们按序分格子。首先是第一个格子,由于前面我们将所有列平移到了图像的右侧,此时该国土是一个上下对称的图形。因此为了能在后面分格子时具有一定的规律性,我们先将第一个格子选为下图右侧中最左边的那个,如下所示:
图5
接下来分第二个格子。由于第一个格子“占据”了上图所示的国土的上下对称线,因此接下来第二个格子可以占据的位置为其余部分,分别是第一行、第二行、第三行、第四行(或者对称线以下的对称部分)。这里先不考虑对称线的下半部分,我们选择第四行的第一个格子(也可以选择第二个),可以得到如下图所示的分法:

图6
注:这里不考虑对称线的下半部分是出于讨论分析时更容易理解的角度。实际上,怎么选都是可以的。
接下来分别选择第三行、第二行的第一个格子作为此过程中诸侯所分到的格子,如下所示:
图7
当剩下最后一个诸侯时,若我们假设其只能在最后一列中选择格子,那么其可选的位置如下:
图8
由于前面四个诸侯分别将第二行、第三行、第四行、第五行“占据”,因此留给最后一个诸侯的位置就只能是最后一列(含有2*n-1行)中,除开这4行的其余位置。所以在这种情况下,我们不难得出第五个诸侯有 (2*n-1) - (k-1) = 2*n - k = 2*5 - 5 = 5种安置方式。即,在固定了前面k-1个诸侯所分得的格子后,第k个诸侯的安置方式有2*n - k种。
该公式的正确性可以验证。比如当k=6时,我们可以让前面四个诸侯的放置方式与k=5时的放置方式一致,同时将第五个诸侯放置在第四列的最后一排,如下图所示:
图9
这时,对于最后一个诸侯而言,若我们假设其只能在最后一列中选择格子,不难看出其能放置的位置情况如下:
图10
将n=5,k=6带入公式有:2*n - k = 2*5 - 6 = 4,符合上图中的情况。
经检验,将k取值为其余合法值时上述公式的正确性依然成立。
这里需要注意,前面的分析为我们得到的结论是:“在固定了前面k-1个诸侯所分得的格子后,第k个诸侯的安置方式有2*n - k种”。这不是最终的递推式,而只是其中一种特殊情况,因此在前面的分析中我们总是做了这样的假设“对于最后一个诸侯,若我们假设其只能在最后一列中选择格子”。
客观来看,如果我们要在一个给定了n和k的值的国土中为诸侯安置土地,则在所有的安置方案中,对于该国土的最后一列而言,其只会有两种情况:
① 最后一列没有任何诸侯;
② 最后一列被安置了某位诸侯(也就是前面所讨论的情况)。
基于此,若我们假设一个变量f[i][j],其含义为:在前面i列中,安置j个诸侯有f[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列的长度)。
该式便是求解本题最终的递推式。
现在我们需要做的最后一步是确定初值。由于n最小为1,k最小为0,而由上面的递推式,当k取值为0时(即j=0时)会发生数组越界,因此j在实际的递推过程中需要从1开始迭代。此时有:

f[1][1] = f[0][1] + f[0][0] * ( len(1) - 0) = f[0][1] + f[0][0]

显然,f[0][j]=0(j为正整数),因为这表示没有格子给诸侯分配,因此需要设所有的f[0][j]为0。由于当n=1,k=1时,显然只有一种分配方案,所以f[1][1]=1,因此可以反推出f[0][0]=1。而对于任意的f[i][0](i为正整数),这都表示“在前面i列中不安置诸侯的分配方法”,所以f[i][0]=1。
故此,我们便确定好了f数组的初值。下面给出本题的完整代码:




完整代码

#include<iostream>
using namespace std;const int N = 205;
const int MOD = 504;
int f[N][N]; 
int len[N]; 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;// 为数组设置初值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] %= MOD;}cout<<f[2*n-1][k]<<endl; return 0;}

END


这篇关于【洛谷】 P1240 诸侯安置(递推)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/861567

相关文章

uva 568 Just the Facts(n!打表递推)

题意是求n!的末尾第一个不为0的数字。 不用大数,特别的处理。 代码: #include <stdio.h>const int maxn = 10000 + 1;int f[maxn];int main(){#ifdef LOCALfreopen("in.txt", "r", stdin);#endif // LOCALf[0] = 1;for (int i = 1; i <=

高精度计算(代码加解析,洛谷p1601,p1303)除法待更新

目录 高精度加法 高精度减法 高精度乘法 高精度加法 我们知道在c++语言中任何数据类型都有一定的表示范围。当两个被加数很大时,正常加法不能得到精确解。在小学,我们做加法都采用竖式方法。那么我们也只需要按照加法进位的方式就能得到最终解。 8 5 6+ 2 5 5-------1 1 1 1 加法进位: c[i] = a[i] + b[i];if(c[i] >=

洛谷 凸多边形划分

T282062 凸多边形的划分 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 先整一个半成品,高精度过两天复习一下补上 #include <iostream>#include <algorithm>#include <set>#include <cstring>#include <string>#include <vector>#include <map>

能量项链,洛谷

解释:  环形dp问题还是考虑将环拉直,可以参考我上一篇文章:环形石子合并 [2 3 5 10 2] 3 5 10 将环拉直,[]内是一个有效的区间,可以模拟吸收珠子的过程,         如[2 3 5] <=>(2,3)(3,5)    2是头,3是中间,5是尾 len >= 3:因为最后[2 10 2]是最小的可以合并的有效区间 len <= n + 1因为[2 3

HLJUOJ1128 HDU2046(数学递推)

1128: 递推求解专题练习三 Time Limit: 1 Sec   Memory Limit: 128 MB Submit: 8   Solved: 6 [ Submit][ Status][ Web Board] Description 在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数。 例如n=3时,为2× 3方格,骨牌的铺放方案有三

洛谷P5490扫描线

0是最小的数字,将一个线段看成一个区间,对于一个矩形,从下扫到上,入边为1,而出边为-1,意思是将这个区间上的所有点加1(区间修改).把线段表示为Line[i],其中记录了l,r,h,tag,左右端点,高度,入边还是出边(1或-1) 那么每次区间修改后不为0的区间它的值可能是1,2,3或者是其它数字,这不好统计,可以将它转化一下,0是不是表示没有被覆盖过的地方,我们只要统计0的个数然后用总长减去

挤牛奶洛谷uasco

题目描述 三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶。第一个农民在300秒(从5点开始计时)给他的牛挤奶,一直到1000秒。第二个农民在700秒开始,在 1200秒结束。第三个农民在1500秒开始2100秒结束。期间最长的至少有一个农民在挤奶的连续时间为900秒(从300秒到1200秒),而最长的无人挤奶的连续时间(从挤奶开始一直到挤奶结束)为300秒(从1200秒到1500秒)。

【递推】【DP】-HDU-2064-汉诺塔③

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2064 题目描述:从最左边移到最右边柱子的过程中必须经过中间柱子。 解题思路: 进ACM组时候的考试题,当时虐我的题终于被我虐回来了。。一眼看出方程,1A了。。。呵呵。。满足一下我的虚荣心,,,抚慰一下受挫的心灵吧。 AC代码: #include <iostream>using names

【递推】【DP】-HDU-1996-汉诺塔⑥

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1996 题目描述:问三个柱子上放N个盘子,一共可能有多少种组合?(可以有柱子不放,放的时候依然满足下面盘子比上面盘子大) 解题思路: 对于放N个盘子,ans [ N ] = 3 + 6 * f ( N ) +6 * g ( N ) 这三项依次代表这N个盘子分成一堆,两堆,三堆时有多少种可能。排列

【递推】【DP】-HDU-1995-汉诺塔⑤

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1995 题目描述:计算汉诺塔中某个盘子的移动次数 解题思路: 想了好久,突然顿悟! 思路如下,所谓递推,即是前者与后者存在直接联系,由前者可以直接推出后者,因此必须有一端是已知的,这题里显然最下面的盘子只被动了一次,这就是开端,然后我们考虑紧挨着的两张盘子的递推关系,发现上面盘子是紧挨着的下面盘