Problem
问题
Consider a grid such as the one shown. We wish to mark k intersections in each of n rows and n columns in such a way that no 4 of the selected intersections form a rectangle with sides parallel to the grid. Thus for k = 2 and n = 3, a possible solution is:
想象一个n行n列的网格,我们要在每行每列都标注k个交点,使得这些交点中的任意4个所构成的矩形(如果可以构成矩形),都不存在与网格线平行的边。比如k=2和n=3的情况,可能的解如下:
It can easily be shown that for any given value of k, k2-k+1 is a lower bound on the value of n, and it can be shown further that n need never be larger than this.
易知,对于任意的k,n的最小值为:k2-k+1。更进一步来讲,n也不会比这个值更大。
Write a program that will find a solution to this problem for k≤32, k-1 will be 0, 1 or prime.
写一个程序,计算所有k≤32的解。k-1可能是0,1或任何素数。
Input and Output
输入与输出
Input will consist of some values for k, one of each line. For each value of k, output will consist of n lines of k points indicating the selected points on that line. Print a blank line between two values of k.
输入包括一组k的值,每个一行。对每一个k值,要输出n行数,各行中的每个数代表在该行中选择的交点编号。每组k值的输出之间要有一个空行。
Sample input
输入示例
2
1
Sample output
输出示例
1 2
1 3
2 3
1
Analysis
分析
有趣的找规律题目,我想不会有人用递归的办法去暴力破解吧?一拿到题,我当时就懵了,想了好久没有理出头绪来。后来觉得光靠空想根本不可能解决问题,便找来尘封已久的棋盘来摆了一下,结果豁然开朗。强烈建议您先别急着看解析,想办法摆一下棋盘,或着在坐标纸上画一下。这是个锻炼思维的好机会,直接看解析就很没意思了。
摆的方法是非常直接的,第一行直接摆在前面,下面每行在不于上面的行冲突(构成矩形)的情况下,尽量往前摆就OK了。下图是k=5的情况:
仔细看看,应该发现规律了吧。还没有?再看下图:
从第6行开始,以下每4行为一组,每组编号用i表示,i=(0, 1, 2, 3);每组中的4行用j表示,j=(0, 1, 2, 3);每行中从第6列开始,右边每4列为一组,每组编号用s表示,s=(0, 1, 2, 3)。这里用s是为了避免和上面已经出现的k产生混淆。
(i, j, s)为一个三元组,现在要求的就是(0, 0, 0)到(3, 3, 3),一共4×4×4=64个值。i=0时,规律很明显,(0, j, s)=5 + s * (k - 1) + 1。式中第一个5是空出前面的5个格,s * (k - 1)计算当前4×4方块的起点,后面加1是为了保证数字从1开始,而不是0。
再看(1, 0, k)到(2, 0, k),第一个4×4方块(橙色)和上面的(黄色)排列是一样的,只是第二个方块(蓝色)比上面的(青色)向右移动了一格。由此可推知公式:
- (j + (s * i)) % m + s * m + k + 1
再用下面的两排验证此公式,都成功。现在才明白为什么n=k2-k+1,原来n=k+(k-1)(k-1),展开即得原式。这道题虽然解开了,但是这个公式我没有能够证明,这里面可能还蕴涵着更深奥的数理逻辑问题。如果您知其所以然,望不吝赐教!
Solution
解答
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <iostream> using namespace std; //主函数 int main( void ) { //循环处理每一组输入 for ( int k, nFirst = 1; cin >> k; nFirst = 0) { //不是第一次输出时,前面要加空行 if (nFirst == 0) { cout << endl; } int m = k - 1; //输出头部的k行 for ( int i = 0; i < k; ++i) { cout << 1; for ( int j = 1; j < k; ++j) { cout << ' ' << i * m + j + 1; } cout << endl; } //以下算法请参照图示及相关文档 for ( int i = 0; i < m; ++i) { for ( int j = 0; j < m; ++j) { cout << i + 2; for ( int s = 0; s < m; ++s) { cout << ' ' << (j + (s * i)) % m + s * m + k + 1; } cout << endl; } } } return 0; } |
![]() |
作者:王雨濛;新浪微博:@吉祥村码农;来源:《程序控》博客 -- http://www.cnblogs.com/devymex/ 此文章版权归作者所有(有特别声明的除外),转载必须注明作者及来源。您不能用于商业目的也不能修改原文内容。 |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!