[USACO06FEB] Backward Digit Sums G/S
题面翻译
有这么一个游戏:
写出一个 1 ∼ n 1\sim n 1∼n 的排列 a a a,然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少 1 1 1,直到只剩下一个数字位置。
下面是一个例子:
- 3 , 1 , 2 , 4 3,1,2,4 3,1,2,4;
- 4 , 3 , 6 4,3,6 4,3,6;
- 7 , 9 7,9 7,9;
- 16 16 16。
最后得到 16 16 16 这样一个数字。
现在想要倒着玩这样一个游戏,如果知道 n n n,以及最后得到的数字的大小 s u m sum sum,请你求出最初序列 a i a_i ai(应该是一个 1 ∼ n 1\sim n 1∼n 的排列)。若答案有多种可能,则输出字典序最小的那一个。
我们称序列 a = ⟨ a 1 , a 2 , ⋯ , a n ⟩ a=\lang a_1,a_2,\cdots,a_n\rang a=⟨a1,a2,⋯,an⟩ 的字典序小于序列 b = ⟨ b 1 , b 2 , ⋯ , b n ⟩ b=\lang b_1,b_2,\cdots,b_n\rang b=⟨b1,b2,⋯,bn⟩ 的字典序,当且仅当存在一个位置 p p p,满足 a 1 = b 1 , a 2 = b 2 , ⋯ , a p − 1 = b p − 1 , a p < b p a_1=b_1,a_2=b_2,\cdots,a_{p-1}=b_{p-1},a_p<b_p a1=b1,a2=b2,⋯,ap−1=bp−1,ap<bp。
题目描述
FJ
and his cows enjoy playing a mental game. They write down the numbers from
1
1
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
N=4
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
N
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 , s u m n,sum n,sum。
输出格式
输出包括一行,为字典序最小的那个答案。
当无解的时候,请什么也不输出。
样例 #1
样例输入 #1
4 16
样例输出 #1
3 1 2 4
提示
- 对于 40 % 40\% 40% 的数据, 1 ≤ n ≤ 7 1\le n\le 7 1≤n≤7;
- 对于 80 % 80\% 80% 的数据, 1 ≤ n ≤ 10 1\le n \le 10 1≤n≤10;
- 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 12 1\le n \le 12 1≤n≤12, 1 ≤ s u m ≤ 12345 1\le sum\le 12345 1≤sum≤12345。
思路
这道题看到数据范围比较小,我们第一意识肯定是用dfs暴搜并模拟该过程,那么我们就很容易写出以下代码:
#include<iostream>
using namespace std;
const int N = 15;
int ans[N],cnt;
int t[N];
int n,m;
bool st[N];
bool f;
bool check(){
for(int i=1;i<=m;i++){
t[i]=ans[i];
}
//模拟家琪的过程
for(int i=m;i>=1;i--){
for(int j=1;j<i;j++){
t[j]+=t[j+1];
}
}
return t[1]==n;
}
void dfs(int u){
if(f)return;
if(u==m+1&&check()&&!f){
for(int i=1;i<=m;i++){
cout<<ans[i]<<" ";
}
f=true;
return;
}
for(int i=1;i<=m;i++){
if(!st[i]){
ans[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
}
}
}
int main(){
cin>>m>>n;
//m为限制个数
dfs(1);
return 0;
}
但这个代码只能得80分,因为我们每到m+1的时候都要check一遍,很浪费时间,因此我们得优化。
对于这种由上下相加得到的数我们可以考虑杨辉三角优化。
所以我们就可以优化掉check函数。
代码
//对于这种上下有明显关系的我们就得考虑到杨辉三角
#include<iostream>
using namespace std;
const int N = 15;
int ans[N],cnt;
int t[N];
int n,m;
int w[N][N];//杨辉三角
bool st[N];
bool f;
// bool check(){
// for(int i=1;i<=m;i++){
// t[i]=ans[i];
// }
// //模拟家琪的过程
// for(int i=m;i>=1;i--){
// for(int j=1;j<i;j++){
// t[j]+=t[j+1];
// }
// }
// return t[1]==n;
// }
// void dfs(int u){
// if(f)return;
// if(u==m+1&&check()&&!f){
// for(int i=1;i<=m;i++){
// cout<<ans[i]<<" ";
// }
// f=true;
// return;
// }
// for(int i=1;i<=m;i++){
// if(!st[i]){
// ans[u]=i;
// st[i]=true;
// dfs(u+1);
// st[i]=false;
// }
// }
// }
void dfs(int u,int res){
if(res>n)return;
if(f)return;
if(u==m+1&&res==n&&!f){
for(int i=1;i<=m;i++){
cout<<ans[i]<<" ";
}
f=true;
return;
}
for(int i=1;i<=m;i++){
if(!st[i]){
st[i]=true;
ans[u]=i;
dfs(u+1,res+i*w[m][u]);
st[i]=false;
}
}
}
int main(){
cin>>m>>n;
w[1][1]=1;
for(int i=2;i<=m;i++){
for(int j=1;j<=i;j++){
w[i][j]=w[i-1][j]+w[i-1][j-1];
}
}
//m为限制个数
dfs(1,0);
return 0;
}