题目链接:http://poj.org/problem?id=1020
题意:
首行是t,下面t行每行是一个case,第一个数s表示给你一个边长为s的正方形大蛋糕,第二个数n表示把这个大蛋糕分成n块小正方形蛋糕,后面跟n个数表示这n个蛋糕的边长。
问是否能刚好分完。是输出KHOOOOB! ,不是输出HUTUTU!
个人感觉这道搜索题有些难...看了别人的代码才了解了思路。
参考博客:http://blog.csdn.net/lyy289065406/article/details/6683250 感谢!
大体的思路就是以一个col数组来标记每列已经占用了多少。然后先放大蛋糕,以左下优先的顺序去不断尝试切小蛋糕,切不了要求的则回溯。
根据数据可以推出大蛋糕最大的大小是40 * 40。
具体见代码。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int s, n;
int sizenum[11]; //各大小小蛋糕的数量
int col[41]; //该列被切了多少
int dfs(int dep) {
if(dep == n) return 1; //全切完退出
int minc = 50, pcol;
int i;
for(i = 1; i <= s; i++) { //找到被切最少的列,因为要满足从下到上的原则
if(minc > col[i]) {
minc = col[i];
pcol = i;
}
}
int size, p;
for(size = 10; size > 0; size--) { //从最大块开始尝试切下
if(sizenum[size] == 0) continue;
if(s - col[pcol] >= size && s - pcol + 1 >= size) { //以该列为起点能切下这块大小
for(p = pcol; p <= pcol + size - 1; p++) { //看该列以后所需的列是否也都满足被切条件
if(col[p] != col[pcol]) break;
}
if(p == pcol + size) { //满足则切下,修改标记
for(p = pcol; p <= pcol + size - 1; p++) col[p] += size;
sizenum[size]--;
if(dfs(dep + 1)) return 1; //若为假则下面回溯
sizenum[size]++;
for(p = pcol; p <= pcol + size - 1; p++) col[p] -= size;
}
}
}
return 0;
}
int main () {
int t;
scanf("%d", &t);
while(t--) {
scanf("%d %d", &s, &n);
memset(sizenum, 0, sizeof(sizenum));
memset(col, 0, sizeof(col));
int i, area = 0, sid;
for(i = 1; i <= n; i++) {
scanf("%d", &sid);
area += sid * sid;
sizenum[sid]++;
}
if(area != s * s) { //连面积都不相等直接退出
printf("HUTUTU!\n");
continue;
}
if(dfs(0)) printf("KHOOOOB!\n");
else printf("HUTUTU!\n");
}
return 0;
}