剪枝 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;
}
posted @ 2025-02-19 14:45  AKgrid  阅读(17)  评论(0)    收藏  举报