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. 全局对象的构造函数会在main函数之前执行?

    #include <iostream> using namespace std; class CTest { public: CTest() { cout << "构 ...

  2. LeetCode 664. Strange Printer 奇怪的打印机(C++/Java)

    题目: There is a strange printer with the following two special requirements: The printer can only pri ...

  3. The current test process

    样机测试 测试前: 工作内容: 1.需求分析.编写.评审: 项目开工会由项目负责人参加,参加会议时做好笔记,对项目的功能类似,功能模块,测试时间点有个大致的了解. 原始需求进行需求文档细化:按照模块进 ...

  4. [Python]获取win平台文件的详细信息

    import win32api def getFileProperties(fname): """ 读取给定文件的所有属性, 返回一个字典. ""&q ...

  5. WeChall_Prime Factory (Training, Math)

    Your task is simple:Find the first two primes above 1 million, whose separate digit sums are also pr ...

  6. HDU_4403

    http://acm.hdu.edu.cn/showproblem.php?pid=4403 数值不大,暴力枚举,枚举每一种划分,然后枚举每一种等号位置. #include<iostream&g ...

  7. 题解 NOI2004【郁闷的出纳员】

    \[ Preface \] 之前用 treap 打,交了四遍才过. 自学了 fhq treap 后,才意识到是一道 fhq treap 板子题,直接码上,一遍就过. 本题解提供的是 fhq treap ...

  8. Java程序员必备英文单词

    列表中共有769个单词,这些单词是从JDK.Spring.SpringBoot.Mybatis的源码中解析得到,按照在源码中出现的频次依次排列,页面中的单词是出现频次大于1000的.单词的音标.翻译结 ...

  9. 《Head first设计模式》之命令模式

    命令模式将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 一个家电公司想邀请你设计一个家电自动化遥控器的API.这个遥控器有7个 ...

  10. javascirpt获取随机数

    /* getran(min, max, n): 获取min与max之间的随机数 n: n保留浮点数数量 */ function getran(min, max, n){ return Number(( ...