描述
话说有这么一个图形,只有两种符号组成(‘+’或者‘-’),图形的最上层有n个符号,往下个数依次减一,形成倒置的金字塔形状,除第一层外(第一层为所有可能情况),每层形状都由上层决定,相邻的符号相同,则下层的符号为‘+’,反之,为‘-’;如下图所示(n = 3 时的两种情况):
如果图中的两种符号个数相同,那这个三角形就是幸运三角形,如上图中的图(2).
输入
有多组测试数据(少于20组)。
每行含一个整数n
(0<n<20)。
输出
输出相应的幸运三角形个数。
样例输入
3
4
样例输出
4
6
dfs和 bfs 要想提高速度,降低复杂度,就必须尽可能的剪枝。
这个题我一开始用深度优先搜索dfs一边搜索,一遍判断是否矛盾。但是在n>=15,虽然答案是对的,但是严重超时。后来看了一下讨论区,被某位大牛给震惊了,竟然用操作二进制的方法解出来了。
我的代码dfs:
#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
int n=0;
int a=0,b=0;
bool NoMaoDun(stack<int> s)
{
int ss[n+1][n+1];
//stack<char> s=st;
int size=s.size();
int num=n;
int i;
for(i=1;i<=n;i++)
{
if(size>num)
{
size=size-num;
num=num-1;
continue;
}
break;
}
if(i==1)
return true;
int j=0;
int k=size;
j=size;
size=i;
for(i;i>=1;i--)
{
for(;j>=1;j--)
{
ss[i][j]=s.top();
s.pop();
}
j=n-i+1+1;
}
j=k;
for(i=size;i>=2;i--)
{
for(;j>=1;j--)
{
if((ss[i][j]==1 && ss[i-1][j]!=ss[i-1][j+1] ) || (ss[i][j]==0 && ss[i-1][j]==ss[i-1][j+1] ) )
return false;
}
j=n-i+1+1;
}
return true;
}
int main()
{
int flag=1;
int num=0;
int k;
stack<int> mystack;
bool qqq;
while(scanf("%d",&n)!=EOF)
{
a=0;
b=0;
flag=1;
num=0;
if((1+n)*n/2%2==1)
{
printf("%d\n",0);
continue;
}
mystack.push(1);
a++;
while(!mystack.empty())
{
mystack.push(1);
a++;
if((a+b)==(1+n)*n/2+1)
{
num++;
//printf("a %d b %d \n",a,b);
mystack.pop();
a--;
if(mystack.top()==1)
{
mystack.pop();
a--;
mystack.push(0);
b++;
flag=0;
}
else if(mystack.top()==0)
{
mystack.pop();
b--;
flag=0;
}
}
if(a>(1+n)*n/4 || b>(1+n)*n/4)
qqq=true;
else
qqq=!NoMaoDun(mystack);
//qqq=!NoMaoDun(mystack);
while((qqq || flag==0) && !mystack.empty())
{
flag=1;
k=mystack.top();
mystack.pop();
if(k==0)
{
b--;
flag=0;
continue;
}
if(k==1)
{
a--;
mystack.push(0);
b++;
}
if(a>(1+n)*n/4 || b>(1+n)*n/4)
qqq=true;
else
qqq=!NoMaoDun(mystack);
}
}
printf("%d\n",num);
}
return 0;
}
大牛的思想:
我们可以用一个bit表示一个’+’或者’-‘,0表示‘+’,1表示‘-’。这样表示用一个整数表示一行。
比如,n=4,那么第一行的数s的枚举就是从数0到15,然后要根据第一行的数计算第二行的数,其实,第二行的数就是s^(s>>1),每一位和相邻位的异或,范围从0到7,再继续计算,直到最后一行的数为0或者1.也就是说,对n=4.建立一个s[4]的数组可以记录下每一行的数,而每一行得数也容易从上一行得到。接下来,问题就变成了计算每行代表的那个整数所代表的二进制数中1的个数。
按照大牛思想编写的代码:
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
int main()
{
int n=0;
while(scanf("%d",&n)!=EOF)
{
int a[n];
//0表示'-' , 1表示'+'
int num=0;
int i;
int k=0;//k记录1的个数
if((n+1)*n/2%2==1)
{
printf("%d\n",0);
continue;
}
int q=pow(2,n);
for(a[0]=0;a[0]<q;a[0]++)
{
k=0;
for(i=0;i<n;i++)
{
k+=0x01 & (a[0]>>i);
}
for(i=1;i<=n-1;i++)
{
a[i]=a[i-1]^(a[i-1]>>1);
for(int j=0;j<n-i;j++)
{
k+=0x01 & (a[i]>>j);
}
if(k>(n+1)*n/4)
break;
}
if(i>=n && k==(n+1)*n/4)
{
num++;
}
}
printf("%d\n",num);
}
return 0;
}