T1 2121. 简单游戏

(File IO): input:easy.in output:easy.out

时间限制: 1000 ms  空间限制: 262144 KB  具体限制

Goto ProblemSet

题目描述

      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

数据范围限制

数据保证有解。请注意加粗字体!
对于30%的数据,保证该组里的每个N都不超过10。
对于100%的数据,保证有每个N不超过20,且每组数据的个数不超过10。

Solution

首先可以发现,对于一个长度为n的排列,经过了n-1次邻项相加后,第i项被加了C(n-1,i)次(二项式定理).

所以可以先预处理杨辉三角形。

Algorithm1

使用next_permutation方便的计算地排列

但是没有剪枝...很慢的

Code1

简单的垃圾代码

 #pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#define IL inline
using namespace std;
int tria[][]={
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
};
//triangle
int ans;
int arr[],t,n;
IL int read()
{
char ch;int x=;
ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
}
int main()
{
freopen("easy.in","r",stdin);
freopen("easy.out","w",stdout);
n=read();
t=read();
do{
for(int i=;i<=n;i++)
arr[i]=i;
do{
ans=;
int i;
for(i=;i<=n&&ans<=t;i++)
ans+=arr[i]*tria[n][i];
if(ans==t&&i>n){
for(int j=;j<=n;j++){
printf("%d",arr[j]);
if(j<n) printf(" ");
}
cout<<endl;
break;
}
}while(next_permutation(arr+,arr+n+));
n=read();
t=read();
}while(n!=||t!=);
return ;
}

Code1

Algorithm2

使用dfs,对每一位进行判断,同时标记use(是否使用过此数)

这比next_permutation好在可以尽情剪枝——只要你能想到

Code2

这是剪枝1:如果当前的和(前depth个数的和)已经超过了t,就跳出。

也可以把

if(sum>t) return;

放到for循环里(这不是废话吗)

可以减少一点分支

按照题解上的说法,这样子只有40分(果然……)

 #pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#define IL inline
using namespace std;
int tria[][]={
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
};
int use[];
int ans;
int arr[],t,n;
IL int read()
{
char ch;int x=;
ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
}
bool succ=;
IL void dfs(int depth,int sum)
{
if(sum>t) return;
if(succ) return;
if(depth==n){
if(sum==t)
{
succ=;
for(int j=;j<=n;j++){
printf("%d",arr[j]);
if(j<n) printf(" ");
}
printf("\n");
}
return;
}
for(int i=;i<=n;i++)
{
if(!use[i])
{
use[i]=;
arr[depth+]=i;
dfs(depth+,sum+i*tria[n][depth+]);
arr[depth+]=;
use[i]=;
}
}
}
int main()
{
// freopen("easy.in","r",stdin);
// freopen("easy.out","w",stdout);
n=read();
t=read();
do{
succ=;
dfs(,);
n=read();
t=read();
}while(n!=||t!=);
return ;
}

Algorithm3

这是剪枝2:

由于杨辉三角形有对称性

比如,枚举到 2 4 1 3 时,其sum(邻项合并后的结果)会与 2 1 4 3 相同

那么可以加入以下判断:

if(depth>(n/) && i<arr[n-depth]) continue;

代码翻译

如果当前要枚举的数即将放的位置超过了总长度的一半,且即将枚举的数值i小于与它关于这个二项式对称的那一项,那就说明这两项如果调换,总值不会发生变化,那么就不考虑这种情况。

这句话真长……

Code3

 #pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#define IL inline
using namespace std;
int tria[][]={
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
};
int use[];
int arr[],t,n;
IL int read()
{
char ch;int x=;
ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
}
bool succ=;
IL void dfs(int depth,int sum)
{
if(succ) return;
if(depth==n){
if(sum==t)
{
succ=;
for(int j=;j<=n;j++){
printf("%d",arr[j]);
if(j<n) printf(" ");
}
printf("\n");
}
return;
}
for(int i=;i<=n;i++)
{
if(!use[i])
{
if(depth>(n/) && i<arr[n-depth]) continue;
if(sum+i*tria[n][depth+]>t) continue;
use[i]=;
arr[depth+]=i;
dfs(depth+,sum+i*tria[n][depth+]);
arr[depth+]=;
use[i]=;
}
}
}
int main()
{
// freopen("easy.in","r",stdin);
// freopen("easy.out","w",stdout);
n=read();
t=read();
do{
succ=;
dfs(,);
n=read();
t=read();
}while(n!=||t!=);
return ;
}

Code3

好的,果然如题解所说,这个剪枝并不能加分。

Algorithm4

这是剪枝3:枚举前,就当前还未使用的数字来讲,可以将它们重新排序,对应还没使用的系数,可以算出已选择的数字不变的情况下,让之后几个数排列组合,再乘以对应系数的和的最大值与最小值。

貌似我讲的不太清楚。

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

举个栗子

若N=4,T=16:

当枚举arr[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。

Code4

 #pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#define IL inline
using namespace std;
int tria[][]={
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
};
int use[];
int tarr[],ttria[];
int arr[],t,n;
IL int read()
{
char ch;int x=;
ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') x=(x<<)+(x<<)+(ch^),ch=getchar();
return x;
}
bool succ=;
IL void dfs(int depth,int sum)
{
if(succ) return;
if(depth==n){
if(sum==t)
{
succ=;
for(int j=;j<=n;j++){
printf("%d",arr[j]);
if(j<n) printf(" ");
}
printf("\n");
}
return;
} int s=;
for(int i=;i<=n;i++)
{
if(use[i]) continue;
tarr[++s]=i;
}
for(int i=depth+;i<=n;i++)
ttria[i-depth]=tria[n][i];
sort(tarr+,tarr+s+);
sort(ttria+,ttria+s+);
int maxs=,mins=;
for(int i=;i<=s;i++)
{
maxs+=tarr[i]*ttria[i];
mins+=tarr[i]*ttria[s-i+];
}
if(t<mins+sum||t>maxs+sum) return; for(int i=;i<=n;i++)
{
if(!use[i])
{
if(depth>(n/) && i<arr[n-depth]) continue;
if(sum+i*tria[n][depth+]>t) continue;
use[i]=;
arr[depth+]=i;
dfs(depth+,sum+i*tria[n][depth+]);
arr[depth+]=;
use[i]=;
}
}
}
int main()
{
// freopen("easy.in","r",stdin);
// freopen("easy.out","w",stdout);
n=read();
t=read();
do{
succ=;
dfs(,);
n=read();
t=read();
}while(n!=||t!=);
return ;
}

Impression

好辛苦呀~~~

纪中20日c组模拟赛T1 2121. 简单游戏的更多相关文章

  1. 纪中20日c组模拟赛

    赛后感想 多写点东西总是好的,但是在最后,算法就不要改动了(就这样我少了10分) 题解 T1 2121. 简单游戏 T2 2122. 幸运票

  2. 纪中18日c组模拟赛

    T2 GMOJ2127. 电子表格 (File IO): input:excel.in output:excel.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制   ...

  3. 纪中21日c组模拟赛

    AWSL  AWSL  AWSL  AWSL AWSL  AWSL  AWSL  AWSL AWSL  AWSL  AWSL  AWSL AWSL  AWSL  AWSL  AWSL 题解传送 T1  ...

  4. 纪中20日c组T2 2122. 【2016-12-31普及组模拟】幸运票

    2122. 幸运票 (File IO): input:tickets.in output:tickets.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制 Goto P ...

  5. 洛谷P1880 [NOI1995]石子合并 纪中21日c组T4 2119. 【2016-12-30普及组模拟】环状石子归并

    洛谷P1880 石子合并 纪中2119. 环状石子归并 洛谷传送门 题目描述1 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石 ...

  6. 纪中23日c组T2 2159. 【2017.7.11普及】max 洛谷P1249 最大乘积

    纪中2159. max 洛谷P1249 最大乘积 说明:这两题基本完全相同,故放在一起写题解 纪中2159. max (File IO): input:max.in output:max.out 时间 ...

  7. 纪中23日c组T3 2161. 【2017.7.11普及】围攻 斐波那契数列

    2161. 围攻 (File IO): input:siege.in output:siege.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制   Goto Prob ...

  8. 纪中21日c组T2 2117. 【2016-12-30普及组模拟】台风

    2117. 台风 (File IO): input:storm.in output:storm.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制 Goto Proble ...

  9. 纪中21日c组T1 1575. 二叉树

    1575. 二叉树 (File IO): input:tree.in output:tree.out 时间限制: 1000 ms  空间限制: 262144 KB  具体限制   Goto Probl ...

随机推荐

  1. 微信小程序没有找到可以构建的npm包

    如果是云开发小程序 ,cmd窗口进入到小程序根目录下的miniprogram目录, 输入npm init 输入包的相关属性,随便输入即可. 完成后会出现一个package.json文件. 如果是其他类 ...

  2. svg图片在vue脚手架vue-cli怎么使用

    第一种 使用vue2-svg-icon npm install vue2-svg-icon --save-dev` 下载之后在mian.js引入 名字可以随便起,这里我起icon 引入svg资源 这时 ...

  3. 理解numpy中ndarray的内存布局和设计哲学

    目录 ndarray是什么 ndarray的设计哲学 ndarray的内存布局 为什么可以这样设计 小结 参考 博客:博客园 | CSDN | blog 本文的主要目的在于理解numpy.ndarra ...

  4. 一、Shell概述

    什么是Shell 解释Shell脚本名词之前,我们先来说下什么是Shell? Shell是一个命令解释器,它在操作系统的最底层,负责直接与用户对话,把用户的输入解释给操作系统,并处理各种各样的操作系统 ...

  5. VUE报表开发

    因为在项目中经常开发一些报表,并且业务.逻辑其实都有大部分的重复部分. 所以将这些常用的模块抽象出来.并且可视化操作.封装成一款报表开发工具. 先看一下项目的一些效果:数据单项绑定 可视化操作: 数据 ...

  6. ubuntu 14.04 安装gvim 后报出warning

    (gvim:3572): GLib-GObject-WARNING **: Attempt to add property GnomeProgram::sm-connect after class w ...

  7. ajax面试要点

    目录 目录 ajax是什么? 优点 缺点 ajax的工作原理 如何创建一个ajax(ajax的交互模型) ajax过程中get和post的区别 同步和异步的区别 JavaScript 的同源策略 如何 ...

  8. 调用winpcap发送路由器公告

    #include <stdlib.h> #include <stdio.h> #include <pcap.h> #pragma comment(lib, &quo ...

  9. 【2020】DBus,一个更能满足企业需求的大数据采集平台

    功能远超Sqoop.DataX.Flume.Logatash.Filebeat等采集工具 注:由于文章篇幅有限,完整文档可扫免费获取 深知其他组件的局限性,才能彰显DBus的优越感 当前有很多数据采集 ...

  10. 【题解】 2月19日 厦门双十中学NOIP2014模拟D2 T2 采药人接水果

    [问题描述] 采药人虽然 AFO(SU),但他在闲暇的时候还是可以玩一玩接水果(cat)的.但他渐渐发现 cat 好像有点太弱智.于是他不想浪费他的智商,于是决定写一个程序帮他玩. cat 是这样玩的 ...