洛谷P1118 数字三角形

这道题是典型的深搜题,应用全排列遍历的方法可以在无特判的条件下完成。其中的几个知识点是需要复习的。

杨辉三角

杨辉三角定义在此不表,其为一个形似下表的数表:

1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
(以下略)

可以观察到,除每层第一个和最后一个均为1外,其中每一个数都来源于上一层与其“对应”的两个数之和,在本题中也就能对应出如下结果:

a b c d e
a+b b+c c+d d+e
a+2b+c b+2c+d c+2d+e
a+3b+3c+d b+3c+3d+e
a+3b+6c+3d+e
(以下略)

可以看出,每一层的每一个数,其值都相当于从自己开始,系数为杨辉三角对应层数的数的多项和式,那么要计算出最后结果,就用对应层数的杨辉三角辅助求和就可以了。就是二项式定理

STL:next_permutation

不想写麻烦的全排列怎么办?STL中有一个神奇的函数可以帮你做到这一点:next_permutation
这个函数会按照数字大小从小到大依次生成下一个排列,效果与DFS是一致的。因此,我们可以利用这个特性来根块更便捷地做这道题。

说明next_permutation的几点特性:
(1)其返回值是bool类型,这进一步方便了我们在本题中使用这个函数,若不存在下一个全排列这说明寻找失败;
(2)其调用方式和其他STL函数基本相同,即输入需要生成下一个排列的起止点,左闭右开。

优化思路

在经过杨辉三角的优化后,我们能在 O ( n ) O(n) O(n)的时间内将每次询问的答案计算出来,但仍不够。这时,我们需要对全排列的枚举进行优化。思考:若在计算答案过程中,答案的值已经超过了需要的值,那么之后的所有值都已经没有意义,因此不必计算。同时,在加入某值时若超出所需范围,则由我们的枚举顺序的性质(从小到大)就可知,在其之后的数也一定会超过范围。因此我们可以对出现问题的一位到末尾的这一段按从大到小的顺序排序,再执行next_permutation,就可以将出项问题一段整体跳过。

AC代码

#include
#include
using namespace std;
int n,s,v[15],ans,pre[15],tri[13][15];
void triangle()//处理杨辉三角
{
	for(int i=1;i<=12;i++) {tri[i][1]=1;tri[i][i]=1;}
	for(int i=3;i<=12;i++)
		for(int j=2;j<i;j++)
			tri[i][j]=tri[i-1][j-1]+tri[i-1][j];
}
bool cmp(int x,int y){return x>y;}
bool solve(int a,int b)
{
	for(int i=1;i<=n;i++) pre[i]=i; 
	do
	{
		ans=0;
		for(int j=1;j<=n;j++) 
		{
			ans+=pre[j]*tri[n][j];
			if(ans>b) 
			{
				sort(pre+1+j,pre+1+n,cmp); 
				break;
			}
		}
		if(ans==b) return 1;
	}while(next_permutation(pre+1,pre+1+n)); 
	return 0;
}
int main()
{
	scanf("%d%d",&n,&s);
	triangle();
	bool f=solve(n,s);
	if(f) for(int i=1;i<=n;i++) printf("%d ",pre[i]);
	return 0;
}
STL大法好!!!

你可能感兴趣的:(题解)