2016.6.3

最近期末複習…沒時間總結博客了 = =!

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;
}

点赞