最近期末複習…沒時間總結博客了 = =!
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;
}
2.棋盤問題
題目鏈接: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;
}