Input
Output
Sample Input
2
4 8 1 1 1 1 1 3 1 1
5 6 3 3 2 1 1 1
Sample Output
KHOOOOB!
HUTUTU!
题意: 给出num个小正方形, 问能否拼成n*n大小的大正方形.
解题思路:
1. 一开始我想到是动态规划-状态压缩, 但是很快打消念头, 因为每个小正方形大小可能不一.
无法子问题重叠, 不符合动态规划条件.(我没想出来怎么DP)
2. 搜索, 范围最大16个小正方形, 大小1<=size<=10, 16*10*10 = 40*40, 范围搜索还是可以.
设: a[i]表示size = i的正方形的个数;
g[i]表示第i行用了前g[i]列(这样可以节省空间 和方便搜索)
3. 搜索策略:
(1). 大的正方形比较难处理, 小的正方形比较容易, 先从大的开始.
(2). 每次都从最左上角开始找出可以放置的位置.
4. 最后要记住小正方形面积当然要等于大正方形才可以拼成.
代码:
#include
#include
#include
using namespace std;
#define MAX 41
#define MAXSIZE 11
int n, num;
int a[MAXSIZE], g[MAX]; //sqrt(16*10*10) = 40
int sum;
int getMin()
{
int minR, size = MAX;
for(int i = 1; i <= n; ++i)
{
if( g[i] < size )
{
size = g[i];
minR = i;
}
}
return minR;
}
bool dfs(int cur)
{
int i, j, minR;
bool flag;
if( cur == num ) return true;
minR = getMin(); //找出最左上角的位置
for(i = MAXSIZE; i >= 1; --i)
{
flag = false;
if( a[i] != 0 && minR+i-1 <= n && g[minR]+i <= n ) //范围和个数
{
for(j = minR; j < minR+i; ++j) //判断是否可以放置这个正方形.
if( g[j] > g[minR] ) break;
if( j == minR+i ) flag = true; //标记可以放置
}
if( flag )
{
a[i]--;
for(j = minR; j < minR+i; ++j)
g[j] += i; //记录已经放置后状态
if( dfs(cur+1) ) return true;
for(j = minR; j < minR+i; ++j)
g[j] -= i;
a[i]++;
}
}
return false;
}
int main()
{
// freopen("input.txt", "r", stdin);
int caseNum;
scanf("%d", &caseNum);
while(caseNum--)
{
memset(a, 0, sizeof(a));
memset(g, 0, sizeof(g));
scanf("%d %d", &n, &num);
int temp;
sum = 0;
for(int i = 0; i < num; ++i)
{
scanf("%d", &temp);
a[temp]++;
sum += temp*temp;
}
if( sum != n*n ) //面积不一样当然拼不成
printf("HUTUTU!\n");
else
{
if( dfs(0) == true )
printf("KHOOOOB!\n");
else
printf("HUTUTU!\n");
}
}
return 0;
}