65,186
社区成员




#include <stdio.h>
#include <math.h>
#include <time.h>
#include <assert.h>
const MAXWIDTH = 120; //最大宽度
const MAXBASE = 3; //最小正方形最大尺寸
const RANK = 9; //阶数
const MARGIN = 10; //长宽最大差值(0表示正方形)
int square[MAXWIDTH]; //NUM个数的平方,优化效果微小
int result[RANK]; //可能的排列结果【0, rank-1】,从小到大
int width, height; //宽、高
int count = 0; //穷举记数
int flag[RANK]; //代表被使用的顺序号(索引);值[0,rank-1],rank表示未使用,>rank做标记用于输出结果,例:flag[RANK-1]=0,表示最大的正方形放第一个
int high[MAXWIDTH]; //已被填放的高度表,比如9×10的长方形放置一个5×5,则数据为:[5 5 5 5 5 0 0 0 0 0]
//注意:这两个数组用于递归,每个递归使用退出后需要还原原值
void perfectrect();
void check();
void search(int n, int index, int sum); //枚举正方形组合
bool searchrect(int n, int left); //判断能否填充
int main(int argc, char * argv[])
{
int i;
//预处理
for(i=1; i<=MAXWIDTH; i++)
{
square[i] = i * i;
}
perfectrect();
printf("Complete!\n");
return 0;
}
void perfectrect()
{
int sum;
int i;
static count2 = count;
time_t t;
time_t t2 = 0;
for(width=20; width<MAXWIDTH; width++)
{
for(height=width; height<=width+MARGIN; height++)
{
sum = height*width;
for(i=1; i<=MAXBASE; i++)
{
result[0] = i;
search(i+1,1, sum-square[i]);
}
}
t = clock();
printf("%d %10d %10d %10d %10d\n", width, count, t, count-count2, t-t2);
count2 = count;
t2 = t;
}
}
/* 穷举所有可能组合,放入result[RANK],并调用check()检查
n: 当前数最小值
index: 当前数索引[0, RANK-1]
sum: 后几位数累加和(含当前数)
*/
void search(int n, int index, int sum)
{
int i;
for(i=n;sum >=square[i]*(RANK-index); i++)
{
if(index == RANK-1 )
{
if(sum == square[i])
{
result[index] = i;
check();
break;
}
}
else
{
result[index] = i;
search(i+1, index+1, sum-square[i]);
}
}
}
//判断能否拼成指定长方形
void check()
{
count++;
int i, tmp;
for(i=0; i<RANK-1; i++)
flag[i] = RANK; //未使用
//第一个放置最大的
flag[RANK-1] = 0;
tmp = result[RANK-1];
for(i=0; i<tmp; i++)
high[i] = tmp;
// for(i=tmp; i<width; i++) //如果对第一行优化,则不需要这个
// high[i] = 0;
//放置第二个正方形,递归搜索
searchrect(1, tmp);
}
/* 穷举能放置的正方形,返回: true 能放,false 不能
n: 第n+1个正方形
left: 第一行时存放其起点,否则为0
*/
bool searchrect(int n, int left)
{
int i, j;
int pos, w, h; //最低处起始位置,高度,宽度
int tmp;
//对第一行优化处理
if(left != 0)
{
for(i=RANK-2; i>=0; i--) //从大到小放置([RANK-1]已被使用)
{
if(flag[i] < n)//是否已使用
continue;
if(result[i] > width-left) //放不下,下一个
continue;
flag[i] = n;
tmp = result[i];
for(j=0; j<tmp; j++)
high[j+left] = tmp;
if(tmp == width-left) //相等,搜索第二行
{
if(searchrect(n+1, 0)) //成功
{
flag[i] = RANK; //恢复标记
return true;
}
}
else //继续第一行
{
if(searchrect(n+1, left+tmp)) //成功
{
flag[i] = RANK; //恢复标记
return true;
}
}
flag[i] = RANK; //恢复标记
}
return false;
//assert(!"第一行排列出错");
}//是否第一行
//非第一行
//获取插入位置和大小
h = height+1;
bool exist = false;
for(i=0; i<width; i++)
{
if(exist && high[i] == h)
{
w++;
}
else if(high[i] < h)
{
h = high[i];
pos = i;
w = 1;
exist = true;
}
else
{
exist = false;
}
}
//遍历
for(i=RANK-2; i>=0; i--) //从大到小放置([RANK-1]已被使用)
{
if(flag[i] < n)//是否已使用
continue;
if(n == RANK-1) //完美长方形
{
if(w == result[i] && height == h+result[i])
{
for(j=0; j<RANK-1; j++)
{
for(int k=0; k<RANK; k++)
if(flag[k]==j)
printf("%d, ", result[k]);
}
printf("%d\n", result[i]);
return true;
}
else
continue;
}
if(w < result[i]) //放不下,下一个
continue;
tmp = result[i];
if(height < h+tmp) //高越界
continue;
flag[i] = n;
for(j=0; j<tmp; j++)
high[j+pos] += tmp ;
if(searchrect(n+1, 0)) //成功
{
flag[i] = RANK; //恢复标记
for(j=0; j<tmp; j++)
high[j+pos] -= tmp ;
return true;
}
else //不成功,恢复标记后转下一个
{
flag[i] = RANK; //恢复标记,好像多余
for(j=0; j<tmp; j++)
high[j+pos] -= tmp ;
}
}//逐个判断
return false;
}
18 14
4 10
15 7 1 9
8
30 25
3 8 17
27 11 2
13 15
/*
┏━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━┓
┃ ┃ ┃ ┃
┃ ┃ ┃ ┃
┃ X 2n+1┃ ┃ ┃
┃ ┃ ┃ ┃
┃ ┃ n+1 ┃ n+2 ┃
┃ ┃ ┃ ┃
┃ ┃ ┃ ┃
┃ ┣━━━━━━┳┫ ┃
┃ ┃ ┣┻━━━━━━━━┫
┃ ┃ ┃ ┃
┃ ┃ n ┃ ┃
┃ ┃ ┃ ┃
┃ 15 ┃ ┃ ┃
┣━━━━━━━━━━━━━┻━━┳━━━┫ n+3 ┃
┃ ┃ ┃ ┃
┃ ┃ 4 ┃ ┃
┃ ┃ ┃ ┃
┃ ┣━━━┻━━━━━━━━━┫
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┃ n+11 ┃ ┃
┃ ┃ n+7 ┃
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┃ ┃ ┃
┗━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┛
*/