最近期末复习...没时间总结博客了 = =!
1.深搜 生日蛋糕
题目链接:http://cxsjsx.openjudge.cn/hw201615/B/
这是一道很经典的深搜加剪枝的题目
首先澄清题意,最大表面积应该是每个蛋糕的侧面积加上最底层蛋糕的底面积。
接下来就是怎样剪枝的问题,代码里写的很详细,直接贴了
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<stack>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
int MinArea = 1 << 30; // 存最优表面积;
int MinV[30]; // 存第一层到该层最小体积;
int MinA[30]; // 存第一层到该层最小面积;
int N, M; // 体积,层数;
int area = 0; // 存正在搭建的表面积;
int MaxV(int n, int r, int h) // 当有n层时,可以有的最大体积;其中r为最大半径,h为最大高度;
{
int v = 0;
for (int i = 0; i < n; i++)
v += (r - i)*(r - i)*(h - i);
return v;
}
void dfs(int v, int n, int r, int h)
{
if (n == 0)// 说明一种情况搭建层数已经完成;
{
if (v)
return;
else{
MinArea = min(MinArea, area); // 可以搭建,则更新最优解;
return;
}
}
if (v <= 0)//剪枝1:体积不够,退出;
return;
if (MinV[n] > v)//剪枝2:搭建n层的最小体积比提供的体积大,即无法搭建;退出;
return;
if (area + MinA[n] >= MinArea)//剪枝3:当前搭建表面积加上前n层最小的表面积比最优解更大,则可以退出;
return;
if (h < n || r < n)//剪枝4:最大半径,或者最大高度比层数还要少,则就说明已经无法继续搭建了;
return;
if (MaxV(n, r, h) < v) //剪枝5:这n层可以搭建的最大体积都比提供的体积要小,说明无法按要求搭建;
return;
for (int rr = r; rr >= n; rr--)
{ // 从最大半径开始枚举搜索;
if (n == M)
area = rr*rr; // 底面积;
for (int hh = h; hh >= n; hh--)
{ // 从最大高度开始枚举;
area += 2 * rr * hh; // 搭建的面积更新;
dfs(v - rr * rr * hh, n - 1, rr - 1, hh - 1);
area -= 2 * rr * hh; // 回溯;
}
}
}
int main()
{
while (~scanf("%d%d", &N, &M))
{
MinV[0] = 0;
MinA[0] = 0;
for (int i = 1; i <= M; i++)
{
MinV[i] = MinV[i - 1] + i*i*i; // 前i层最小体积;
MinA[i] = MinA[i - 1] + 2 * i*i; // 前i层最小面积;
}
if (MinV[M] > N)
printf("0\n");
else
{
int MaxH = (N - MinV[M - 1]) / (M*M) + 1; // 底层最大高度;
int MaxR = sqrt(double(N - MinV[M - 1]) / M) + 1; // 底层最大半径;
int area = 0;
MinArea = 1 << 30;
dfs(N, M, MaxH, MaxR);
if (MinArea == 1 << 30)
printf("0\n");
else printf("%d\n", MinArea);
}
}
return 0;
}
题目链接:http://cxsjsx.openjudge.cn/hw201615/A/
八皇后问题的变式。深搜的经典题目,需要熟练掌握回溯等方法。
#include <iostream>
#include <memory.h>
using namespace std;
bool row[8] = { false }; bool board[8][8];
int n;
int Find(int left, int col)
{ // 无法继续放子的情况: 剩余的棋子数比接下来的行数要多
if (left > n - col) //注意不能是等于
return 0;
// 放完最后一个棋子,摆放成功一种方案
if (left == 0)
return 1;
// 1. 当前行不放棋子,棋子数不变,列加1
int result = Find(left, col + 1);
// 2. 当前行放棋子
for (int i = 0; i < n; i++)
if (!row[i] && board[i][col]) // 计算在第i列放棋子的方案数
{
row[i] = true;
result += Find(left - 1, col + 1);
row[i] = false; //回溯
}
return result;
}
int main()
{
int k; char line[9];
while (cin >> n >> k)
{
if (n == -1 && k == -1)
break;
for (int i = 0; i < n; i++)
{
cin >> line; //字符串输入一行
for (int j = 0; j < n; j++)
board[i][j] = (line[j] == '#'); //棋盘区域“#”置1,可以放棋子
}
cout << Find(k, 0) << endl;
}
return 0;
}