【深搜題目】百練 2815 城堡問題,poj 1190 搭蛋糕 poj1321 棋盤問題

目錄

城堡問題

POJ—1190 搭蛋糕 

poj1321   棋盤問題   


 

城堡問題

題目鏈接:http://bailian.openjudge.cn/practice/2815/

描述

     1   2   3   4   5   6   7  
   #############################
 1 #   |   #   |   #   |   |   #
   #####---#####---#---#####---#
 2 #   #   |   #   #   #   #   #
   #---#####---#####---#####---#
 3 #   |   |   #   #   #   #   #
   #---#########---#####---#---#
 4 #   #   |   |   |   |   #   #
   #############################
           (圖 1)

   #  = Wall   
   |  = No wall
   -  = No wall


圖1是一個城堡的地形圖。請你編寫一個程序,計算城堡一共有多少房間,最大的房間有多大。城堡被分割成mn(m≤50,n≤50)個方塊,每個方塊可以有0~4面牆。

輸入

程序從標準輸入設備讀入數據。第一行是兩個整數,分別是南北向、東西向的方塊數。在接下來的輸入行裏,每個方塊用一個數字(0≤p≤50)描述。用一個數字表示方塊周圍的牆,1表示西牆,2表示北牆,4表示東牆,8表示南牆。每個方塊用代表其周圍牆的數字之和表示。城堡的內牆被計算兩次,方塊(1,1)的南牆同時也是方塊(2,1)的北牆。輸入的數據保證城堡至少有兩個房間。

輸出

城堡的房間數、城堡中最大房間所包括的方塊數。結果顯示在標準輸出設備上。

樣例輸入

4 
7 
11 6 11 6 3 10 6 
7 9 6 13 5 15 5 
1 10 12 7 13 7 5 
13 11 10 8 10 12 13 

樣例輸出 

9


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int r,c;
int m[60][60];
int color[60][60];
int MaxroomArea=0;
int Numroom=0;
int RoomArea;
void Dfs(int i,int j)
{
    if(color[i][j])
        return ;
    color[i][j]=Numroom;
    RoomArea++;
    if((m[i][j]&1)==0) Dfs(i,j-1);
    if((m[i][j]&2)==0)Dfs(i-1,j);
    if((m[i][j]&4)==0)Dfs(i,j+1);
    if((m[i][j]&8)==0)Dfs(i+1,j);

}
int main()
{
    cin>>r>>c;
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            cin>>m[i][j];
    memset(color,0,sizeof(color));
    for(int i=0;i<r;i++)///每個方格只搜索一次
        for(int j=0;j<c;j++)
        {
            if(!color[i][j])
            {
                Numroom++;
                RoomArea=0;
                Dfs(i,j);
                MaxroomArea=max(RoomArea,MaxroomArea);
            }
        }
    cout<<Numroom<<endl;
    cout<<MaxroomArea<<endl;
    return 0;
}

 

POJ—1190 搭蛋糕 

http://poj.org/problem?id=1190

思路:

讓你求一個體積爲N,層數爲M的最小表面積的蛋糕。 思路就是要找到一種最優的策略,使得在這種策略下,每層的蛋糕半徑,高度恰好會使整個蛋糕的表面積最小。所以就應該枚舉每一層蛋糕的半徑和高度,找到一組最優解。枚舉的半徑和高度的範圍如何確定? 因爲體積和層數已經給出,我們可以特殊考慮來求出最大可能半徑最大可能高度。 搜索順序怎麼安排? 既然是搭蛋糕,枚舉的時候應該從情況少的枚舉情況多的,所以應該從底層往上搭蛋糕,枚舉底層蛋糕的可能半徑 高高度,然後利用深搜(即遞歸)依次往上確定每層蛋糕的半徑和高度。 所以DFS函數中應該這麼寫 DFS(int v,int n,int r,int h)意思就是我們要用n層的蛋糕來湊成體積爲v的蛋糕。 深搜結束的邊界條件就是 層數n=0,此時v=0,此時就可以求出minArea ,在不斷深搜比較求最小值即可找出最小的表面積。  重要的是要用到非常細的剪枝。

最優性剪枝:當前的表面積已經比我之前求出的最小變面積還大就剪枝或者預見到如果繼續以當前情況繼續往下找沒前途,也剪枝

可行性剪枝: 1.:搭建過程中預見到再往上搭,高度已經無法安排,或者半徑 已經無法安排,則停止搭建。這句話的意思就是我們要保證最下層蛋糕的半徑範圍和高度範圍。如果說底層蛋糕半徑是1 高度是1 那麼上層蛋糕就沒得可選了,所以我們要保證底層蛋糕的範圍是 

n<=r<=maxR    n<=h<=maxH

                        2.:搭建過程中發現還沒搭的那些層的體積,一定會超過還缺的 體積,則停止搭建。意思就是我們搭建的過程中,前面搭建的體積過大,導致上面n層未搭建的體積即便是取最小的體積情況也會超過還缺的體積。所以這種情況要剪枝。

                         3.:搭建過程中發現還沒搭的那些層的體積,最大也到不了還缺 的體積,則停止搭建。意思就是 我們搭建的過程中前面的體積搭的太小了 即便是我們上面n層未搭建的體積取最大的體積情況也達不到還缺的體積。這種情況也要剪枝。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int N,M;
int maxR,maxH;//最大半徑 和最大高度
int minarea=1<<30;//最小表面積
int area=0;//當前表面積
void init()//求最大半徑和最大高度
{
    int sum=0;
    for(int i=1;i<=M-1;i++)
        sum+=i*i*i;
    maxR=sqrt((double)((N-sum)/M));
    maxH=(N-sum)/(M*M);
}

void dfs(int v,int n,int r,int h)
//要用n層去湊體積v,最底層半徑不能超過r,高度不能超過h
{
    if(n==0)
    {
        if(v) return;
        else {minarea=min(area,minarea);return;}
    }
    if(v<=0) return;
    for(int rr=r;rr>=n;rr--)
    {
        if(n==M)//如果當前搭建的是最下層,則那麼蛋糕上層所有的表面積就確定了。
            area=rr*rr;
        for(int hh=h;hh>=n;hh--)
        {

            //最優性剪枝和預見性剪枝
            if(area+2*rr*hh>=minarea||(area+2*rr*hh+n*(n-1)*(2*n-1)/3>minarea))
                continue;
            //可行性剪枝:前面搭建的體積過大,導致上面n層未搭建的體積即便是取最小的體積情況也會超過還缺的體積
            //1^3+2^3+...(n-1)^3 = n*n*(n-1)*(n-1)/4
            if(n*n*(n-1)*(n-1)/4>v-rr*rr*hh)
                continue;
            //可行性剪枝:前面的體積搭的太小了 即便是我們上面n層未搭建的體積取最大的體積情況也達不到還缺的體積
            //(rr-1)^2*(hh-1)+(rr-2)^2*(hh-2)+...
            int sum=0;
            for(int i=1;i<=n-1;i++)
                sum+=(rr-i)*(rr-i)*(hh-i);
            if(sum<v-rr*rr*hh)
                continue;
            //area這條語句一定要寫在剪枝的後面!!!
            area+=2*rr*hh;
            dfs(v-rr*rr*hh,n-1,rr-1,hh-1);
            area-=2*rr*hh;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>N>>M;
    init();
    dfs(N,M,maxR,maxH);
    if(minarea==(1<<30))
        cout<<0<<endl;
    else
        cout<<minarea<<endl;
    return 0;
}

 

poj1321   棋盤問題   

更深刻的理解了dfs的步驟

鏈接

遞歸給我的感覺就是,求一種問題,有很多類似無數種數不清的情況,例如n皇后,還有今天做的棋盤問題,一般這類問題都用到遞歸的思想。 遞歸給我的感覺就是,枚舉第一行,從第一行第一個可行的情況開始一直往下遞歸下去,知道找不到結果或者找出結果,然後不斷回溯,一直回溯到剛開始第一行第一個可行的狀態下,所以這個過程就相當於在第一行第一個可行的狀態下我把所有能到目標的種類都找了出來,接下來就應該從第一行第二個可行的結果往下遞歸,依次類推下去就能找出所有可行結果。  當然這個過程中一定有大量不可行的,所以我感覺難的題目就要寫非常細的剪枝。

還有今天學習到 更加加深的理解了dfs中寫法的含義,

我覺得應該這樣寫,大致是這樣的吧,以後做題慢慢體會吧 ,這題耽誤的時間太多了 不浪費時間了啊!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
char m[10][10];
int vis[10];
int num=0;
void dfs(int lay)//lay表示行
{
    //終止條件
    if(k==0)
    {
        num++;
        return;
    }
    if(lay==n)
        return;
    //因爲每行都有兩種可能,放或者不放,所以就有兩種dfs的
    //在這一行放的dfs()
    for(int i=0;i<n;i++)
    {
        if(m[lay][i]=='.'||vis[i])
            continue;
        vis[i]=1;
        k--;
        dfs(lay+1);
        vis[i]=0;
        k++;
    }
    //在這一行不放的dfs
    dfs(lay+1);
}
int main()
{
//    freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    while(cin>>n>>k)
    {
        if(n==-1&&k==-1) break;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                cin>>m[i][j];
        memset(vis,0,sizeof(vis));
        num=0;
        dfs(0);//從第零行開始放
        cout<<num<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章