2121. 【2016-12-31普及组模拟】简单游戏

来源:互联网 发布:网络许可盈利 编辑:程序博客网 时间:2024/04/29 18:27

【题目描述】

 

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 163 90 0



【样例输出】

3 1 2 41 3 2


首先解释一下样例:

1)

3           1            2            4

   3+1=4   1+2=3    2+4=6

          4+3=7    3+6=9

               7+9=16

2)

1          3             2

  1+3=4    3+2=5

          4+5=9



比赛思路:比赛的思路只有暴力了,不管了,果断打了一个爆搜,于是时间超限拿了个40分,如下所述:

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>using namespace std;int a[21][21];int tf,sum,t,n;int s[21];int b[21];bool pd[21];void yanghui(void){     a[1][1]=1;     for (int i=2;i<=20;i++)     {         for (int j=1;j<=i;j++)         {             a[i][j]=a[i-1][j]+a[i-1][j-1];         }     }}void dg(int n,int k){     if (tf==1) return;     if (sum>t) return;     if (sum+(s[n]-s[k-1])*n<t) return;     if ((k-1>(n+1)/2)&&(b[n-k+2]>b[k-1])) return;     if (k==n+1)     {          if (sum==t)          {               for (int i=1;i<=n;i++)               {                   printf("%d ",b[i]);               }               printf("\n");               tf=1;          }          return;     }     else     {         for (int i=1;i<=n;i++)         {             if (pd[i]==false)             {                  sum=sum+i*a[n][k];                  b[k]=i;                  pd[i]=true;                  dg(n,k+1);                  if (tf==1) return;                  sum=sum-i*a[n][k];                  pd[i]=false;             }         }     }}int main(){    freopen("easy.in","r",stdin);    freopen("easy.out","w",stdout);    yanghui();    while (1!=0)    {          memset(pd,false,sizeof(pd));          tf=0;sum=0;          scanf("%d%d",&n,&t);          if ((n==0)&&(t==0))          {               return 0;          }          for (int i=1;i<=n;i++) s[i]=s[i-1]+a[n][i];          dg(n,1);    }    return 0;}


这一个思路其实是正确的,但是时间超限也是一个问题,那只能加优化了,看了看题解,发现有三个剪枝。

剪枝一:加一个小小的优化,就是在确定第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。


wow,看了我大概半个小时后终于明白,于是赶紧码了出来,于是AC。题解如下:

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>using namespace std;int n,t;bool tf=false;int pd[110],da[110];int f[110][110];bool p[110];int min1(int x){int j=0;int minn=0;for (int i=x+1;i<=n;i++) j++,pd[j]=f[n][i];for (int i=1;i<=n-x;i++){for (int j=i+1;j<=n-x;j++){if (pd[i]>pd[j]){int t;t=pd[i];pd[i]=pd[j];pd[j]=t;}}}j=1;for (int i=n;i>=1;i--) {if (p[i]==false) minn=minn+pd[j]*i,j++;}return minn;}int max1(int x){int j=0;int maxx=0;for (int i=x+1;i<=n;i++){j++;pd[j]=f[n][i];}for (int i=1;i<=n-x;i++){for (int j=i+1;j<=n-x;j++){if (pd[i]>pd[j]){int t;t=pd[i];pd[i]=pd[j];pd[j]=t;}}}j=1;for (int i=1;i<=n;i++){if (p[i]==false) maxx=maxx+pd[j]*i,j++; }return maxx;}void dfs(int x,int hh){int i,j,k;if (tf==true) return;if (x==n+1){if (t==hh){for (int i=1;i<=n;i++) printf("%d ",da[i]);printf("\n");tf=true;}return;}else{for (int i=1;i<=n;i++){if (p[i]==false){if (x>n/2) {if (da[n-x+1]>i) continue;}if (hh+i*f[n][x]>t) continue;p[i]=true;if ((t-hh-i*f[n][x]>=min1(x))&&(t-hh-i*f[n][x]<=max1(x))){da[x]=i;dfs(x+1,hh+i*f[n][x]);p[i]=false;da[x]=0;}else p[i]=false;}}}}void yanghui(void){f[1][1]=1;for (int i=2;i<=n;i++){for (int j=1;j<=i;j++){f[i][j]=f[i-1][j-1]+f[i-1][j]; }}}int main(){    freopen("easy.in","r",stdin);freopen("easy.out","w",stdout);while (1!=0){scanf("%d%d",&n,&t);yanghui();if ((n==0)&&(t==0)) return 0;memset(pd,0,sizeof(pd));memset(p,false,sizeof(p));tf=false;dfs(1,0);}return 0;}

其实做完之后才发现这道题很水……(刮开有惊喜)

0 0
原创粉丝点击