题目链接: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; }