我正在尝试解决这个问题,但我找不到解决方案:
给出由排列成N行和M列的正方形组成的板。这块板的瓷砖是覆盖它的瓷砖图案。如果出现以下情况,平铺很有意思:
only tiles of size 1x1 and/or 2x2 are used;
each tile of size 1x1 covers exactly one whole square;
each tile of size 2x2 covers exactly four whole squares;
each square of the board is covered by exactly one tile.
例如,下面的图像显示了一些大小为4行和3列的板的一些有趣的倾斜: http://dabi.altervista.org/images/task.img.4x3_tilings_example.gif
如果板上至少有一个正方形在一个平铺中覆盖有1x1尺寸的瓷砖并且在另一个平铺中覆盖尺寸为2x2的瓷砖,则板的两个有趣的倾斜是不同的。例如,上图中显示的所有倾斜都是不同的。
写一个函数
int count_tilings(int N, int M);
给定两个整数N和M,返回大小为N行和M列的板的不同有趣倾斜数的10,000,007的余数。
假设:
N is an integer within the range [1..1,000,000];
M is an integer within the range [1..7].
例如,假设N = 4且M = 3,则函数应返回11,因为有4个大小为4行和3列的板有11个不同的有趣倾斜:
http://dabi.altervista.org/images/task.img.4x3_tilings_all.gif
表示(4,3),结果为11,对于(6,5),结果为1213。 我尝试了以下但是它不起作用:
static public int count_tilings ( int N,int M ) {
int result=1;
if ((N==1)||(M==1)) return 1;
result=result+(N-1)*(M-1);
int max_tiling= (int) ((int)(Math.ceil(N/2))*(Math.ceil(M/2)));
System.out.println(max_tiling);
for (int i=2; i<=(max_tiling);i++){
if (N>=2*i){
int n=i+(N-i);
int k=i;
//System.out.println("M-1->"+(M-1) +"i->"+i);
System.out.println("(M-1)^i)->"+(Math.pow((M-1),i)));
System.out.println( "n="+n+ " k="+k);
System.out.println(combinations(n, k));
if (N-i*2>0){
result+= Math.pow((M-1),i)*combinations(n, k);
}else{
result+= Math.pow((M-1),i);
}
}
if (M>=2*i){
int n=i+(M-i);
int k=i;
System.out.println("(N-1)^i)->"+(Math.pow((N-1),i)));
System.out.println( "n="+n+ " k="+k);
System.out.println(combinations(n, k));
if (M-i*2>0){
result+= Math.pow((N-1),i)*combinations(n, k);
}else{
result+= Math.pow((N-1),i);
}
}
}
return result;
}
static long combinations(int n, int k) {
/*binomial coefficient*/
long coeff = 1;
for (int i = n - k + 1; i <= n; i++) {
coeff *= i;
}
for (int i = 1; i <= k; i++) {
coeff /= i;
}
return coeff;
}
答案 0 :(得分:2)
由于这是作业,我不会给出完整的解决方案,但我会给你一些提示。
首先,这是一个递归解决方案:
class Program
{
// Important note:
// The value of masks given here is hard-coded for m == 5.
// In a complete solution, you need to calculate the masks for the
// actual value of m given. See explanation in answer for more details.
int[] masks = { 0, 3, 6, 12, 15, 24, 27, 30 };
int CountTilings(int n, int m, int s = 0)
{
if (n == 1) { return 1; }
int result = 0;
foreach (int mask in masks)
{
if ((mask & s) == 0)
{
result += CountTilings(n - 1, m, mask);
}
}
return result;
}
public static void Main()
{
Program p = new Program();
int result = p.CountTilings(6, 5);
Console.WriteLine(result);
}
}
查看在线工作:ideone
请注意,我添加了额外的参数s
。这将存储第一列的内容。如果第一列为空,则s = 0.如果第一列包含一些实心方块,则设置s中的相应位。最初s = 0,但是当放置一个2 x 2图块时,这将填充下一列中的一些正方形,这意味着在递归调用中s将为非零。
masks
变量是硬编码的,但在完整的解决方案中,需要根据m
的实际值进行计算。如果您查看其二进制表示形式,masks
中存储的值会更有意义:
00000
00011
00110
01100
01111
11000
11011
11110
换句话说,它是用m位设置二进制数的位对的所有方法。您可以编写一些代码来生成所有这些可能性。或者由于m只有7个可能的值,您也可以对masks
的所有七种可能性进行硬编码。
然而,递归解决方案存在两个严重问题。
N
。N
这两个问题都可以通过将算法重写为迭代来解决。保持m
不变,并将n = 1
的所有可能值s
的{{1}}结果初始化为1
。这是因为如果您只有一列,则必须仅使用1x1磁贴,并且只有一种方法可以执行此操作。
现在,您可以使用n = 2
的结果为s
的所有可能值计算n = 1
。这可以重复,直到您达到n = N
。该算法在相对于N
的线性时间内完成,并且需要恒定的空间。
答案 1 :(得分:0)
这是一个递归解决方案:
// time used : 27 min
#include <set>
#include <vector>
#include <iostream>
using namespace std;
void placement(int n, set< vector <int> > & p){
for (int i = 0; i < n -1 ; i ++){
for (set<vector<int> > :: iterator j = p.begin(); j != p.end(); j ++){
vector <int> temp = *j;
if (temp[i] == 1 || temp[i+1] == 1) continue;
temp[i] = 1; temp[i+1] = 1;
p.insert(temp);
}
}
}
vector<vector<int> > placement( int n){
if (n > 7) throw "error";
set <vector <int> > p;
vector <int> temp (n,0);
p.insert (temp);
for (int i = 0; i < 3; i ++) placement(n, p);
vector <vector <int> > s;
s.assign (p.begin(), p.end());
return s;
}
bool tryput(vector <vector <int> > &board, int current, vector<int> & comb){
for (int i = 0; i < comb.size(); i ++){
if ((board[current][i] == 1 || board[current+1][i]) && comb[i] == 1) return false;
}
return true;
}
void put(vector <vector <int> > &board, int current, vector<int> & comb){
for (int i = 0; i < comb.size(); i ++){
if (comb[i] == 1){
board[current][i] = 1;
board[current+1][i] = 1;
}
}
return;
}
void undo(vector <vector <int> > &board, int current, vector<int> & comb){
for (int i = 0; i < comb.size(); i ++){
if (comb[i] == 1){
board[current][i] = 0;
board[current+1][i] = 0;
}
}
return;
}
int place (vector <vector <int> > &board, int current, vector < vector <int> > & all_comb){
int m = board.size();
if (current >= m) throw "error";
if (current == m - 1) return 1;
int count = 0;
for (int i = 0; i < all_comb.size(); i ++){
if (tryput(board, current, all_comb[i])){
put(board, current, all_comb[i]);
count += place(board, current+1, all_comb) % 10000007;
undo(board, current, all_comb[i]);
}
}
return count;
}
int place (int m, int n){
if (m == 0) return 0;
if (m == 1) return 1;
vector < vector <int> > all_comb = placement(n);
vector <vector <int> > board(m, vector<int>(n, 0));
return place (board, 0, all_comb);
}
int main(){
cout << place(3, 4) << endl;
return 0;
}
时间复杂度O(n^3 * exp(m))
减少空间使用量尝试位向量。
将时间复杂度降低到O(m*(n^3))
,尝试动态编程。
将时间复杂度降低到O(log(m) * n^3)
尝试划分和征服+动态编程。