您的位置:首页 > Web前端

洛谷Oj-[USACO06FEB]数字三角形-数学 + 剪枝

2017-08-28 17:29 381 查看
问题描述:

有这么一个游戏:

写出一个1~N的排列a[i],然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。下面是一个例子:

3 1 2 4

4 3 6

7 9

16

最后得到16这样一个数字。

现在想要倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列a[i],为1~N的一个排列。若答案有多种可能,则输出字典序最小的那一个。

AC代码:

int a[20],fact[12] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800};//将0到11的阶乘打表
int c(int n,int m)//计算组合数
{
return fact
/ (fact[m] * fact[n - m]);//公式
}
bool cmp(const int &a,const int &b)//自定义比较函数
{
return a > b;
}
int main()
{
int n,sum,ans;
cin >> n >> sum;//输入
for(int i = 1; i <= n; ++i)//从字典序最小开始。1,2,......,n
a[i] = i;
while(true)
{
ans = 0;//重置
for(int i = 1; i <= n; i++)
{
ans += c(n - 1,i - 1) * a[i];//计算最终得到的数字
if(ans > sum)//可行性剪枝
sort(a + i,a + n + 1,cmp);//将a[i...n]降序排列
}
if(ans == sum)//如果找到了答案
{
for(int i = 1; i <= n; i++)//打印
printf("%d ",a[i]);
break;
}
if(next_permutation(a + 1,a + n + 1) == false)//如果已经是最后一个排列了
break;//无解,跳出循环
}
printf("\n");
return 0;
}


解决方法:

当看到最终的数是由一步步加上去的时,想到最终的数中每个数加的次数一定有规律,于是就从n为3,4,5在草稿纸上手推,当得到1 4 6 4 1这组数时,发现这是组合数。最初的想法是暴力,从最小的字典序开始,运用规律,一次次去尝试,结果T了4个点,有点绝望。虽然是在搜索的栏目里,但是我这个写法并不是搜索,剪枝也没有思路。看了题解后发现我还是太naive了。方法可以说是广义上的剪枝吧,加上a[i]后,ans的大小超过了sum,继续累加也一定不会得到答案。由于杨辉三角中间的数大,所以想把a[i]到a
由大到小排列一下,这样下一次重排就将一定不符合题意的那若干种排列跳过。

改天再写个搜索的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: