【深搜题目】百练 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;
}

 

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