纪中生活 1.3

题目 :

Charles和sunny在玩一个简单的游戏。若给出1~n的一个排列A,则将A1、A2相加,A2、A3相加……An-1、An相加,则得到一组n-1个元素的数列B;再将B1、B2相加,B2、B3相加,Bn-2、Bn-1相加,则得到一组n-2个元素的数列……如此往复,最终会得出一个数T。而Charles和sunny玩的游戏便是,Charles给出n和T,sunny在尽可能短的时间内,找到能通过上述操作得到T且字典序最小的1~n的排列。(sunny大声说:“What  an easy game!”,接着几下就给出了解),Charles觉得没意思,就想和你玩,当然,你可以用一种叫做“电子计算机”的东西帮你。

输入:

本题有多组数据,对于每组数据:一行两个整数n(0<n<=20),t即最后求出来的数。两个0表示输入结束。

输出:

 对于每组测试数据输出一行n个整数,用空格分开,行尾无多余空格,表示求出来的满足要求的1~n的一个排列。

样例输入:

4 16
3 9
0 0 

样例输出:

3 1 2 4
1 3 2
对样例解释:
开始排列:3、1、2、4
第一次操作:3+1=4  1+2=3  2+4=6
得到:4、3、6第二次得到:7、9最后就是:16

分析:

题目要求1-N的一个排列A1,A2…An使得C(N-1,0)*A1+C(N-1,1)*A2+….+C(N-1,N-1)*AN=T-------1式,(有点像杨辉三角)

方法很好确定,先把C(N-1,i)求出来,然后只要把每一个位上的数确定好就可以了,所以采用深度优先搜索的方法。

方法:直接搜,用DFS(x,y)表示当前将要确定第x个位置上数,已经确定的和为y,把每种情况都搜出来,然后在递归出口的位置判断1式是否成立,不过很可惜,这种方法得不到分(超时);

剪枝一:加一个小小的优化,就是在确定第X个数时,保证新求出来的y不能大于T,加上这个优化后,可以得40分;

剪枝二:由于C(N-1,i)=C(N-1,N-1-i),具有对称性,题目又要求最小字典序列,所以在枚举时当x>n div 2 时,要保证a[x]>a[N+1-x],这样程序速度会提高,但是该题还是只能得40分;

剪枝三:当枚举到第X个数时,剩余的N-X个数,与剩余的N-X个系数一一对应,让大数和大系数相乘,小数和小系数相乘可以得到最大值,让大数和小系数相乘,小数和大系数相乘可以得到最小值,如果剩余的值不在这个范围内,就不要搜下去,这样可以大大优化,拿样例举例来说:

N=4,T=16。

当枚举a[1]=1时,剩余16-1*1=15,剩余的未放置的数为2,3,4,剩余的系数为1,3,3,这样最大值为4*3+3*3+2*1=23,最小值为4*1+3*3+2*3=19,都超过了15,所以第一个数不能选1。

其实就是dfs+一堆优化。恶心,还不能保证是否过。

 

 code:

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define N 55
 7 using namespace std;
 8 int f[N][N];
 9 int q[N],t[N],x[N];
10 int n=1,m=1;
11 bool b;
12 bool cmp(int x,int y) {
13     return x>y;
14 }
15 void pre() {
16     memset(f,0,sizeof(f));
17     f[1][1]=1;
18     for (int i=2; i<N; ++i) {
19         for (int j=1; j<=i; ++j) {
20             f[i][j]=f[i-1][j-1]+f[i-1][j];
21         }
22     }
23 
24 }
25 bool pd(int dep,int s) {
26     int an=s;
27     for (int i=dep; i<=n; ++i) {
28         x[i-dep]=f[n][i];
29     }
30     sort(x,x+n-dep+1,cmp);
31     int j=0;
32     for (int i=n; i>=1; --i) {
33         if (t[i]==0) {
34             an+=i*x[j];
35             j++;
36         }
37     }
38     if (an<m) {
39         return true;
40     }
41     an=s;
42     j=0;
43     for (int i=1; i<=n; ++i) {
44         if (t[i]==0) {
45             an+=i*x[j];
46             j++;
47         }
48     }
49 
50     if (an>m) {
51         return true;
52     }
53     return false;
54 }
55 void dfs(int dep,int s) {
56     if (b==1) return;
57     if (dep==n) {
58         if (s==m) {
59             for (int i=1; i<=n; ++i) {
60                 printf("%d ",q[i]);
61             }
62             printf("\n");
63             b=1;
64         }
65         return;
66     }
67     if (s>m) {
68         return;
69     }
70     if (pd(dep+1,s)) {
71         return;
72     }
73     if (dep>n/2 && q[dep]<q[n-dep+1]) {
74         return;
75     }
76     for (int i=1; i<=n; ++i) {
77         if (t[i]==0) {
78             t[i]=1;
79             q[dep+1]=i;
80             dfs(dep+1,s+i*f[n][dep+1]);
81             t[i]=0;
82         }
83     }
84 }
85 int main() {
86     //freopen("easy.in","r",stdin);
87     //freopen("easy.out","w",stdout);
88     while(n&&m) {
89         scanf("%d%d",&n,&m);
90         pre();
91         b=0;
92         memset(t,0,sizeof(t));
93         dfs(0,0);
94     }
95     return 0;
96 }

 

这种题优化不能保证过,但你也没有办法。

这道题也是看了题解才过的。

转载于:https://www.cnblogs.com/ouzijun-OJ/p/11384972.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值