题目:小z 的三角形

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

+ + - + - + +
+ - - - - +
- + + + -
- + + -
- + -
- -
+

★数据输入
第一行为一个整数N(0<N<=12),表示符合三角形的大小。
★数据输出
输出所有满足题意的图案和总个数(输出的图案按图案中第一行
的字典序排序。例如:n=2 时,按++,+-,-+,--的顺序,因为第一行
为++的图案不符合题意,故样例只输出了+-,-+,--)符号之间以空格
隔开。
输入示例输出示例

2 + -
--
+
--
-
+
3
Hint
可以枚举第一行’+’和’-’的情况来补充完整整个三角形

此题难点在于如何对第一行进行遍历,判断和实现三角形的输出相对比较简单,可以使用异或的方法来做。
这里使用深度优先搜索的函数递归调用来对第一行的每一种情况进行分析进而实现回溯,在每一个节点的位置上,都有+-两种情况,利用递归进行搜索判断。这里参考了汉森和昭锡的代码。
代码1:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int f[20][20];
int n;
int ans=0;
void done()//done()函数执行判断和输出符合要求的三角形
{
    
    int i,j;
    for(i=2;i<=n;i++)
    {
        for(j=1;j<=n-i+1;j++)
        {
            if(f[i-1][j]!=f[i-1][j+1])f[i][j]=2;
            else f[i][j]=1;
        }
    }
    int cnt1=0,cnt2=0;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=n-i+1;j++)
        {
			if(f[i][j]==1)cnt1++;
            else cnt2++;
		}
    }
    //cout<<cnt1<<" : "<<cnt2<<endl;
    if(2*cnt1==cnt2)ans++;
    else return ;
    
	for(i=1;i<=n;i++)
    {
        for(j=1;j<=n-i+1;j++)
        {
            if(f[i][j]==1)cout<<"+ ";
            else cout<<"- ";
        }
        cout<<endl;
    }
    return ;
        
}
void work(int t)
{
    //cout<<1<<endl;
    if(t>n)
    {
    done();
    return ;
    }//遇到终点执行done()部分
    else
    {
        f[1][t]=1;
        t++;
        work(t);//此时该节点为'+'
        
	t--;
		
        f[1][t]=2;
        t++;
        work(t);//此时该节点为'-'
    }//实现DFS算法函数递归和判断的部分
}
int main()
{
    cin>>n;
    work(1);
    cout<<ans;
    
	return 0;
}

代码1相对较好理解,整串代码的核心是work()函数:

void work(int t)
{
    //cout<<1<<endl;
    if(t>n)
    {
    done();
    return ;
    }//遇到终点执行done()部分
    else
    {
        f[1][t]=1;
        t++;
        work(t);//此时该节点为'+'
        
	t--;//回到位置,归位
		
        f[1][t]=2;
        t++;
        work(t);//此时该节点为'-'
    }//实现DFS算法函数递归和判断的部分
}

work()函数分成两个部分:

第一个部分

if(t>n)
    {
    done();
    return ;
    }//遇到终点执行done()部分

此时无法继续向下搜索,执行done()部分。

第二个部分

else
    {
//第一个小节
        f[1][t]=1;
        t++;
        work(t);//此时该节点为'+'
//第二个小节
	t--;//回到位置,归位
//第三个小节	
        f[1][t]=2;
        t++;
        work(t);//此时该节点为'-'
    }//实现DFS算法函数递归和判断的部分

虽说只有寥寥几行代码,但确实难以理解。
第二个部分可以分成三个小节:第一个小节代表此时停留的这个节点的状态是+,然后进行向下遍历的操作。第三个小节与第一个小节类似,代表此时停留的这个节点的状态是-。第二个小节t--代表归位(在第一个小节遍历它的所有情况结束以后进行了一次多余的t++操作),从而进行接下来的第三个小节的遍历操作。

大概的意思是,此时该节点的状态如果是+,进行DFS搜索接下来(当这个节点状态是+时)所有的情况,然后返回这个节点,再看这个节点此时的状态如果是-,进行DFS搜索接下来(当这个节点状态是-时)所有的情况,搜索结束以后,这个节点不管状态是+,或是-,它接下来所有的可能性都已经搜索过一遍了。
然后返回这个节点之前的节点,再进行判断。从最末尾开始,一直到最初的起点。

DNNR 回归问题的knn 回归中的df_DNNR 回归问题的knn

可以想象成一棵树,从它的果实返回到它的一个枝节,从它的枝节返回到树的主干。再从主干返回到根。
大家可以通过草稿纸上的模拟进行理解。

代码2:

#include<stdio.h>
const int MAX_M =15;
const int MAX_N =15;
int ans[MAX_M][MAX_N];
int cnt = 0;

void dfs(int n,int i)
{
	if(i<n+1)
	{
		ans[1][i]=0;
		
		dfs(n,i+1);
	}
	else
	{
		int j,k;
		int sum1=0,sum2=0;
		
		for(j=2;j<n+1;j++)
		{
			for(k=1;k<n+2-j;k++)
			{
				ans[j][k]=ans[j-1][k]^ans[j-1][k+1];//使用异或
			}
		}
		
		for(j=1;j<n+1;j++)
		{
			for(k=1;k<n+2-j;k++)
			{
				if(ans[j][k])
					sum1++;
				else
					sum2++;
			}
		}
		
		if(sum1==sum2*2)
		{
			cnt++;
			for(j=1;j<n+1;j++)
			{
				for(k=1;k<n+2-j;k++)
				{
					if(k==1)
					{
						if(ans[j][k])
						  printf("-");
						else
						  printf("+");
					}
					else if(ans[j][k])
						printf(" -");
					else
						printf(" +");
				}
				printf("\n");
			}
		}
		return;
	}

	ans[1][i] = 1;
	dfs(n,i+1);
	return ;
}

int main()
{
	int N;
	
	scanf("%d",&N);
	
	dfs(N,1);
	
	printf("%d\n",cnt);
	
	return 0;
}

代码2与代码1的区别在于,它使用了异或来补充三角形。而且把条件判断和输出三角形放在了同一个函数中。

与本题类似的题目:hdoj2510

2016/3/17

To improve is to change, to be perfect is to change often.