hdu 2510 符号三角形【DFS+打表】

符号三角形

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1358    Accepted Submission(s): 726


Problem Description
符号三角形的 第1行有n个由“+”和”-“组成的符号 ,以后每行符号比上行少1个,2个同号下面是”+“,2个异 号下面是”-“ 。计算有多少个不同的符号三角形,使其所含”+“ 和”-“ 的个数相同 。 n=7时的1个符号三角形如下:
+ + - + - + + 
+ - - - - + 
- + + + - 
- + + - 
- + - 
- - 
+
 

Input
每行1个正整数n <=24,n=0退出.
 

Output
n和符号三角形的个数. 
Sample Input
15
16
19
20
0
 


Sample Output
15 1896
16 5160
19 32757
20 59984
 

 


Source
 

我们注重的还是过程,所以我这里详解一下如何DFS~

思路:

我们从三角形的尖端(独自一个符号占一行的那一行)出发,作为起点,起点无非两种符号,一种是以+号出发,一种是以-号出发。这个时候千万别耍小聪明以为以一种出发的方案数*2就行了,其实是不对的,出发点不同,上边的两个符号也注定不同,那是完全不同的两种方案。

出发点我们确定了,之后就是要慢慢的向上爬,假如我们现在爬到了(4,3)我们在判断是否符合建符号图的过程中关键点其实判断一下三个点是否符合条件即可。

不难想出这三个点分别是a(4,3)b(4,2)c(5,2)。如果a==b,那么c一定要是+,如果不是,那么这个情况就pass掉即可。同理,如果a!=b,那么c一定要是-才行。

另外正号个数==负号个数我们在dfs过程中加入一个统计正号负号的元素就行了。

我们分步来实现代码、

首先是出发:

        memset(a,' ',sizeof(a));//每一次进入深搜都要先清空一下数组
        output=0;
        a[n][1]='+';//起点是+
        dfs(a,n-1,1,1,0);//dfs元素分别是:char a[][]也就是图,n-1,1代表当前要确定符号的点x,y。后边的1,0表示正号个数和负号个数
        memset(a,' ',sizeof(a));//下边同理
        a[n][1]='-';
        dfs(a,n-1,1,0,1);
        printf("%d\n",output);
然后是DFS过程:

void dfs(char a[50][50],int x,int y,int zheng,int fu)
{
    if(x==1&&y==n)//如果走到了最后一个要确定符号的点
    {
        for(int i=0;i<2;i++)
        {
            if(i==0)
            {
                a[x][y]='+';
                if(check(x,y)==1)//如果最后一个要确定符号的点也符合条件
                {
                    if(zheng+1==fu)//并且正负号相同
                    output++;//计数器++;
                }
            }
            if(i==1)
            {
                a[x][y]='-';
                if(check(x,y)==1)
                {
                    if(fu+1==zheng)
                    output++;
                }
            }
        }
        return ;
    }
    for(int i=0;i<2;i++)
    {
        if(i==0)
        {
            a[x][y]='+';
            if(check(x,y)==1)
            {
                if(y==n-x+1)//如果走到了这一行的最后一个字符
                {
                    a[x][y+1]='\0';//别忘记这个点
                    dfs(a,x-1,1,zheng+1,fu);//然后进入下一行的dfs
                }
                else
                {
                    dfs(a,x,y+1,zheng+1,fu);
                }
            }
        }
        else//下边同理
        {
            a[x][y]='-';
            if(check(x,y)==1)
            {
                if(y==n-x+1)
                {
                    a[x][y+1]='\0';
                    dfs(a,x-1,1,zheng,fu+1);
                }
                else
                {
                    dfs(a,x,y+1,zheng,fu+1);
                }
            }
        }
    }
}

然后是判断这个点是否符合条件的check函数:
int check(int x,int y)
{
    if(y==1)return 1;//如果是本行第一个字符,是不需要判断的
    else
    {
        if(a[x][y]==a[x][y-1])//同号下边为+
        {
            if(a[x+1][y-1]=='+')
            return 1;
            else
            return 0;
        }
        if(a[x][y]!=a[x][y-1])//异号下边为-
        {
            if(a[x+1][y-1]=='-')
            return 1;
            else
            return 0;
        }
    }
}

加入了print测试函数的完整DFS代码:

#include<stdio.h>
#include<string.h>
using namespace std;
char a[50][50];
int n,output;
int check(int x,int y)
{
    if(y==1)return 1;
    else
    {
        if(a[x][y]==a[x][y-1])
        {
            if(a[x+1][y-1]=='+')
            return 1;
            else
            return 0;
        }
        if(a[x][y]!=a[x][y-1])
        {
            if(a[x+1][y-1]=='-')
            return 1;
            else
            return 0;
        }
    }
}
void print(char a[50][50])
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            printf("%c",a[i][j]);
        }
        printf("\n");
    }
}
void dfs(char a[50][50],int x,int y,int zheng,int fu)
{
   // printf("%d %d\n",zheng,fu);
    if(x==1&&y==n)
    {
        for(int i=0;i<2;i++)
        {
            if(i==0)
            {
                a[x][y]='+';
                if(check(x,y)==1)
                {
                    if(zheng+1==fu)
                    output++;
                    //print(a);
                }
            }
            if(i==1)
            {
                a[x][y]='-';
                if(check(x,y)==1)
                {
                    if(fu+1==zheng)
                    output++;
                    //print(a);
                }
            }
        }
        return ;
    }
    for(int i=0;i<2;i++)
    {
        if(i==0)
        {
            a[x][y]='+';
            if(check(x,y)==1)
            {
                if(y==n-x+1)
                {
                    a[x][y+1]='\0';
                    dfs(a,x-1,1,zheng+1,fu);
                }
                else
                {
                    dfs(a,x,y+1,zheng+1,fu);
                }
            }
        }
        else
        {
            a[x][y]='-';
            if(check(x,y)==1)
            {
                if(y==n-x+1)
                {
                    a[x][y+1]='\0';
                    dfs(a,x-1,1,zheng,fu+1);
                }
                else
                {
                    dfs(a,x,y+1,zheng,fu+1);
                }
            }
        }
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0)break;
        memset(a,' ',sizeof(a));//每一次进入深搜都要先清空一下数组
        output=0;
        a[n][1]='+';//起点是+
        dfs(a,n-1,1,1,0);//dfs元素分别是:char a[][]也就是图,n-1,1代表当前要确定符号的点x,y。后边的1,0表示正号个数和负号个数
        memset(a,' ',sizeof(a));
        a[n][1]='-';
        dfs(a,n-1,1,0,1);
        printf("%d %d\n",n,output);
    }
}

不过这样交是会TLE的,不信大家可以输入一发24,要跑好久的,也是因为N并不大,我们可以选择剪枝(虽然不一定剪枝就能改变TLE的命运),但是因为N不大 ,我们非常可以选择打表这条路。

所以最终的AC代码是这样的:

#include<stdio.h>
#include<string.h>
using namespace std;
int ans[25]={0,0,0,4,6,0,0,12,40,0,0,171,410,0,0,1896,5160,0,0,32757,59984,0,0,431095,822229};
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n==0)break;
        printf("%d %d\n",n,ans[n]);
    }
}












  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值