poj 1020
大致题意也就是给出大正方形和多个小正方形的边长,用多个小正方形能否拼成一个大正方形。
这是一道搜索的题目,如果暴搜的话,边长为23左右就出不来结果过了。看了大牛的思想有很多方法可以剪枝的。
1、 小蛋糕用一个cake[11]的数组来存,cake[i]表示边长为i的蛋糕有cake[i]个,这样可以避免相同的蛋糕多次判断;
2、 用一维的col[41]来代替二维数组,col[i]表示第i行已经用了前col[i]列;
3、 遇到合适情况直接返回,不必要再往下搜索了;
4、 所有的小蛋糕面积和等于大蛋糕面积;
接下来说怎么搜索吧;
1、在col[]数组中找出使用的最小的行minRow,同时也就找出了下一次方小蛋糕的左上顶点;
2、然后在cake[]数组中找可以放置的蛋糕,“可以放置”是一个重要的条件判断;
(1)minRow+i-1<=size(i表示边长为i的蛋糕,size表示大蛋糕的边长)
(2)col[minRow]+i<=size
(3)col[j]+i<=size (minRow<=j<minRow+i-1)
找不到可以放置的直接返回false;
3、放置成功后,重复1,直到放置完毕。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int col[41],cake[11],size,number; int GetMinRow() { int i,minRow,min=41; for(i=1;i<=size;i++) if(col[i]<min) { min=col[i]; minRow=i; } return minRow; } int dfs(int curNum) { int j,i,minRow; bool ok=false; if(curNum==number) return 1; minRow=GetMinRow(); for(j=10;j>0;j--) { ok=false; if(cake[j] && minRow+j-1<=size && col[minRow]+j<=size) { for(i=minRow;i<minRow+j;i++) { if(col[i]>col[minRow]) break; } if(i==minRow+j) ok=true; } if(ok) { cake[j]--; for(i=minRow;i<minRow+j;i++) { col[i]+=j; } if(dfs(curNum+1)) return 1; for(i=minRow;i<minRow+j;i++) { col[i]-=j; } cake[j]++; } } return 0; } int main() { int i,n,sum,c; scanf("%d",&n); while(n--) { memset(cake,0,sizeof(cake)); scanf("%d%d",&size,&number); for(sum=0,i=0;i<number;i++) { scanf("%d",&c); cake[c]++; sum+=c*c; } if(sum!=size*size) { printf("HUTUTU!\n"); continue; } memset(col,0,sizeof(col)); if(dfs(0)==0) printf("HUTUTU!\n"); else printf("KHOOOOB!\n"); } return 0; }