A:棋盘问题
描述
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。输入输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。
输出对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。
样例输入
2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1
样例输出
2
1
来源
蔡错@pku
1 #include <iostream> 2 #include <string> 3 #include <cstdio> 4 #include <memory.h> 5 #include <map> 6 #include <math.h> 7 using namespace std; 8 const int maxn =15; 9 int hangput[maxn], lieput[maxn],chess[maxn][maxn]; 10 char thisline[maxn]; 11 int n, k, sum=0; 12 13 void solve(int hang,int num) { 14 if (num == 0) 15 { 16 sum++; 17 return; 18 } 19 for(int j=1;j<=n;j++) 20 if (chess[hang][j] && !lieput[j]) 21 { 22 //place the chess 23 if (num - 1 == 0) 24 sum++; 25 else 26 { 27 lieput[j] = 1; 28 for (int i = hang + 1; i <= n - num + 2; i++) 29 solve(i, num - 1); 30 lieput[j] = 0; 31 } 32 } 33 } 34 35 void init() { 36 while (scanf("%d%d", &n, &k) && (n != -1 || k != -1)) { 37 sum = 0; 38 for (int i = 1; i <= n; i++) 39 for(int j=1;j<=n;j++) 40 { 41 char ch; 42 cin >> ch; 43 if (ch == '#') 44 chess[i][j] = 1; 45 else chess[i][j] = 0; 46 } 47 for (int i = 1; i <= n - k + 1; i++) 48 solve(i, k); 49 printf("%d ", sum); 50 } 51 } 52 53 int main() 54 { 55 init(); 56 return 0; 57 }
比较水,和后面一道形成了鲜明的对比
B:生日蛋糕
描述
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层生日蛋糕,每层都是一个圆柱体。
设从下往上数第i(1 <= i <= M)层蛋糕是半径为Ri, 高度为Hi的圆柱。当i < M时,要求Ri > Ri+1且Hi > Hi+1。
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令Q = Sπ
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
输入有两行,第一行为N(N <= 10000),表示待制作的蛋糕的体积为Nπ;第二行为M(M <= 20),表示蛋糕的层数为M。输出仅一行,是一个正整数S(若无解则S = 0)。
样例输入
100
2
样例输出
68
提示
圆柱公式
体积V = πR2H
侧面积A' = 2πRH
底面积A = πR2
来源
Noi 99
1 #include <iostream> 2 #include <string> 3 #include <cstdio> 4 #include <memory.h> 5 #include <algorithm> 6 #include <math.h> 7 using namespace std; 8 const int maxn =15; 9 int n,m,smin=999999; 10 const int maxnum = 9999999; 11 12 void solve(int v,int k,int topr,int toph,int S) { 13 if (k == 0) 14 { 15 if (v == 0) 16 smin=min(S,smin); 17 return; 18 } 19 if (S + 2 * (v / topr) > smin) 20 return; 21 if ((unsigned)(topr - 1)*(unsigned)(topr - 1)*(unsigned)(toph - 1)*k < v) 22 return; 23 int topv = v - k * (k - 1) / 2,_min=maxnum; 24 for(int i=topr-1;i>=k;i--) 25 for (int j = k; j < toph; j++) 26 { 27 int _v = i * i*j; 28 if (_v*k < v) 29 continue; 30 if (_v<v / k) 31 continue; 32 if (_v > topv) 33 continue; 34 int s = 2 * i*j; 35 if (s + k*(k-1)/2 > smin) 36 continue; 37 int sums = s + S; 38 if (k == m) 39 sums+=i*i; 40 solve(v - _v, k - 1, i, j, sums); 41 } 42 } 43 44 void init() { 45 scanf("%d%d", &n, &m); 46 int maxr = sqrt(n); 47 solve(n, m, maxr, n,0); 48 printf("%d ", smin); 49 } 50 51 int main() 52 { 53 init(); 54 return 0; 55 }
一开始我把深搜函数设置为返回S值,如果不能就返回一个超大值……果断TLE。后来想是不是这样每次调用min太慢了,改成了void,直接在函数中在边界条件做判断取最小值,快了巨多……
一开始我的剪枝全部在循环内部,我也在想这样剪枝是不是有点弱……果然太弱……
其实……剪枝只要一个就可以了……就是那个 if (S + 2 * (v / topr) > smin) (最优化剪枝)
最难想,但剪掉的最多
其他……感觉不是特别有用……
然后 (unsigned)(topr - 1)*(unsigned)(topr - 1)*(unsigned)(toph - 1)*k < v 不加 unsigned 会WA,因为会出现溢出。感谢洛谷提供的数据TUT
但是那个条件好像没什么用……