剪枝 P1118 [USACO06FEB] Backward Digit Sums GS
P1118 [USACO06FEB] Backward Digit Sums G/S
题目描述
FJ
and his cows enjoy playing a mental game. They write down the numbers from \(1\) to$ N(1 \le N \le 10)$ in a certain order and then sum adjacent numbers to produce a new list with one fewer number. They repeat this until only a single number is left. For example, one instance of the game (when \(N=4\)) might go like this:
3 1 2 4
4 3 6
7 9
16
Behind FJ
's back, the cows have started playing a more difficult game, in which they try to determine the starting sequence from only the final total and the number \(N\). Unfortunately, the game is a bit above FJ
's mental arithmetic capabilities.
Write a program to help FJ
play the game and keep up with the cows.
输入格式
共一行两个正整数 \(n,sum\)。
输出格式
输出包括一行,为字典序最小的那个答案。
当无解的时候,请什么也不输出。
输入输出样例 #1
输入 #1
4 16
输出 #1
3 1 2 4
说明/提示
- 对于 \(40\%\) 的数据,\(1\le n\le 7\);
- 对于 \(80\%\) 的数据,\(1\le n \le 10\);
- 对于 \(100\%\) 的数据,\(1\le n \le 12\),\(1\le sum\le 12345\)。
思路
题目大意是给定序列 \(1\sim n\) ,相邻元素迭代相加,找到一种排列,如果结果为 \(sum\),输出排列。
从第一层往下想,设 \(n=5\) ,第一层序列为 \(a,b,c,d,e\) ,则算到第五层:
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+4b+6c+4d+e
可以发现第五层结果相应系数为 \(1,4,6,4,1\) ,就是杨辉三角第五层结果。
不难证明,第 \(n\) 层结果就是第一层序列乘以相应杨辉三角系数。
所以将第一层进行全排列,直接计算最后一层的数据,如果数据与 \(sum\) 相等直接输出。
全排列可用DFS,也可用 next_permutation 函数。
剪枝
这里使用最优性剪枝,求最后一层结果时,如果前面几个元素已经大于 \(sum\) ,直接跳过。
这里可以确定大于 \(sum\) 时的元素下标 \(i\) ,从第 \(i\) 个元素开始,把序列后的所有的元素从大到小排序。
这里排序是为了跳过部分全排列。
例:\(n=9\) ,排列 \(\{2,1,3,4,5,6,7,8,9\}\),如果前5个元素 \(\{2,1,3,4,5\}\) 已经大于 \(sum\) ,那么后面 \(\{6,7,8,9\}\sim\{9,8,7,6\}\) 都可以跳过,直接从排列 \(\{2,1,3,4,6,5,7,8,9\}\) 开始。
这里把序列 \(\{6,7,8,9\}\) 直接从大到小排序,就变成最后一个要跳过的排列 \(\{9,8,7,6\}\) ,就实现了剪枝。
代码
#include <bits/stdc++.h>
using namespace std;
int n, sum;
int main()
{
cin >> n >> sum;
// 计算杨辉三角
int a[12][12];
for (int i = 0; i < 12; i++)
a[i][i] = a[i][0] = 1;
for (int i = 2; i < 12; i++)
for (int j = 1; j < i; j++)
a[i][j] = a[i - 1][j] + a[i - 1][j - 1];
int v[12];
for (int i = 0; i < 12; i++) v[i] = i + 1;
do{
int ans = 0;
for (int i = 0; i < n; i++){
ans += a[n - 1][i] * v[i];
if (ans > sum){
sort(v + i, v + n,[&](int x, int y){return x > y;});
break;
}
}
if (ans == sum){
for (int i = 0; i < n; i++) {
cout << v[i] << " ";
}
break;
}
}while (next_permutation(v, v + n));
return 0;
}