程序控

IPPP (Institute of Penniless Peasent-Programmer) Fellow

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::
  77 随笔 :: 0 文章 :: 438 评论 :: 0 引用

公告

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的情况,可能的解如下:

 

f1

 

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 k32, 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的情况:

 

f1

 

仔细看看,应该发现规律了吧。还没有?再看下图:

 

f2

 

从第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
解答

#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;
}
posted on 2010-08-20 23:40 Devymex 阅读(...) 评论(...) 编辑 收藏