2026.1.17 作业 - P1118 [USACO06FEB] Backward Digit Sums G/S
题目描述
FJ 和他的奶牛们喜欢玩一个心算游戏。他们将数字从 \(1\) 到 \(N(1 \le N \le 12)\) 按某种顺序写下来,然后将相邻的数字相加,得到一个数字更少的新列表。他们重复这个过程,直到只剩下一个数字。例如,游戏的一种情况(当 \(N=4\) 时)可能是这样的:
3 1 2 4
4 3 6
7 9
16
在 FJ 背后,奶牛们开始玩一个更难的游戏,她们试图从最终的总和和数字 \(N\) 中确定起始序列。不幸的是,这个游戏有点超出了 FJ 的心算能力。
编写一个程序来帮助 FJ 玩这个游戏,并跟上奶牛们的步伐。
输入格式
共一行两个正整数 \(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 : 枚举全排列,模拟生成的过程。效率 \(O(n! \times n^2 )\)
方法 2 : 枚举全排列,利用二项式定理确定 \(a_i\) 的系数为 $C_n^i $ ,效率 \(O(n! )\)
方法 3 : 加入剪枝优化。
#include <bits/stdc++.h>
using namespace std;
int c[20][20],n,m,s[20];
int main() {
c[0][0]=c[1][0]=c[1][1]=1;
for (int i=2;i<15;i++){
c[i][0]=c[i][i]=1;
for (int j=1;j<i;j++) c[i][j]=c[i-1][j-1]+c[i-1][j];
}
scanf("%d%d",&n,&m);
for (int i=0;i<n;i++) s[i]=i+1;
do{
int Ans=0;
for (int i=0;i<n;i++) Ans+=s[i]*c[n-1][i];
if (Ans==m) {
cout<<s[0];
for (int i=1;i<n;i++) cout<<" "<<s[i];
cout<<endl;
return 0;
}
}while(next_permutation(s,s+n));
return 0;
}
代码2: 剪枝优化
#include<iostream>
using namespace std;
int n,sum,a[15],c[15],v[20][20];
bool vis[15];
void dfs(int x,int ss){
if (c[1]!=0) return;
if(x>n){
if (ss!=sum) return;
for(int i = 1; i<=n; i++) c[i] = a[i];
return ;
}
for(int i = 1; i<=n; i++){
if(vis[i]==0){
vis[i] = 1; a[x] = i;
if (ss+i*v[n-1][x]<=sum) dfs(x+1,ss+i*v[n-1][x]);//优化剪枝
a[x] = 0 ; vis[i] = 0;
}
}
}
int main(){
cin>>n>>sum;
v[0][0]=v[1][0]=v[1][1]=1;
for (int i=2;i<15;i++){
v[i][0]=v[i][i]=1;
for (int j=1;j<i;j++) v[i][j]=v[i-1][j-1]+v[i-1][j];
}
dfs(1,0);
if(c[1]==0) { return 0;}
for(int i = 1; i<=n; i++) cout<<c[i]<<" ";
cout<<endl;
return 0;
}
浙公网安备 33010602011771号