[USACO06FEB] Backward Digit Sums G/S(DFS)

[USACO06FEB] Backward Digit Sums G/S

题面翻译

有这么一个游戏:

写出一个 1 ∼ n 1\sim n 1n 的排列 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 1n 的排列)。若答案有多种可能,则输出字典序最小的那一个。

我们称序列 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,,ap1=bp1,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 1n7
  • 对于 80 % 80\% 80% 的数据, 1 ≤ n ≤ 10 1\le n \le 10 1n10
  • 对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 12 1\le n \le 12 1n12 1 ≤ s u m ≤ 12345 1\le sum\le 12345 1sum12345

思路

这道题看到数据范围比较小,我们第一意识肯定是用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;
}
  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

green qwq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值