压行导论
压行是个好东西,本文将以luoguP5661(本代码提交0分)和其余零碎代码为例,向你讲解如何正确压行
原版未压行(格式化邪教):
#include<bits/stdc++.h> using namespace std; int opt,n,ans,top,m=1,k; int t[100010],p[100010],yh[100010],sj[100010]; bool r[100010]; int main() { cin>>n; for(int i=1; i<=n; i++) { cin>>opt>>p[i]>>t[i]; if(opt==0) { yh[++top]=p[i],sj[top]=t[i],ans+=p[i]; } else { k=0; for(int j=m; j<=top; j++) { if(r[j]) continue; if(t[i]+sj[j]>45) m=j; else if(yh[j]>=p[i]) { k=j; r[k]=true; break; } } if(!k) ans+=p[i]; } } cout<<ans; }
0.为什么压行
- 技术含量不高
- 使代码更短
- 可读性更高(划掉)
- 方便调试(划掉)
虽然格式化邪教也有着不少优点,但是它缺少了动手完成的成就感 ,而且,压行,是神教,正道!!!!!!!!!!!!!!!!!!!!!!!
有人说
华丽的压行技巧并不能提升代码水平,只有不到位的压行技巧导致爆零。
但是,压行一定可以保证代码的可读性
I.压行基础
I.I 逗号的使用
当多条赋值语句和函数各占一行时,会让人不爽,这时,逗号出现了,他可以对其进行连接(注意,他的优先级最低,且返回最后一个值)。
原版
k=j; r[k]=true;
压行
k=j,r[k]=true;
I.II 常数替代
原版代码的定义中我们可以发现数据范围反复出现,此时我们可以定义常量。
原版:
int t[100010],p[100010],yh[100010],sj[100010]; bool r[100010];
压行?(应该只是压代码):
//不建议用const,constexpr可以自己识别某表达式是否是常量 //注:constexpr 为C++11关键字 constexpr int N=100010; int t[N],p[N],yh[N],sj[N]; bool r[N];
I.III 删掉大括号
大括号有时候必须得用到,但是它的出现必定会使代码的行数增加三行至以上,当我们用其他压行技巧把大括号里的内容压到只有一行时,删掉他(函数不行)!
例
for (int i=0;i<n;i++) { for(int j=0;j<n;j++) { scanf("%d",&a[i][j]); } }
压行for(int i=0;i<n;i++) for(int j=0;j<n;j++) scanf("%d",a[i][j]);
II. 中级压行
II.I 三目运算符
条件 ? 如果为真执行 : 如果为假执行
里面不能塞代码块哟
if else
可以扔掉了,再注意一下优先级即可。
如max函数
int max(int a,int b) { if(a>b) return a; else return b; }
int max(int a,int b){return a>b?a:b;}
注意:三元运算符不能完全代替if else
,比如for
,while
,goto
,break
,continue
是不能塞进去的
II.II 短路原理
请先看代码:
#include<stdio.h> int main() { int a=100,b=200; if(a>99&&b++) { if(a>200&&b=b-2) ; } printf("%d %d",a,b); return 0; }
输出为100 201
这就是短路性质:
如果第一个条件(左边的条件)为真(true),则不会计算第二个条件(右边的条件),整个表达式被认为为真。
只有当第一个条件为假(false)时,才会继续计算第二个条件。 如果第二个条件为真,整个表达式被认为为真;如果第二个条件也为假,整个表达式被认为为假。
而例如a=b+1
返回的值是计算后的a
,所以通过这种方法我们可以把:
if(a%b==0) { sum++; break; }
改为
if(a%b==0&&++sum) break;//不要sum++防止sum的初始为0而导致条件为假
注:跟科学中的电路同理,都容易使电路(代码)坏掉或无法实现所希望的功能
II.III 熟练的STL
C++中的STL库真的很好用的(不要跟我一样用C++但沉迷于C语言无法自拔)
比如map可以自定义下标 就map<int,int>好像没用对吧,这个下标可以存负数的,内部利用红黑树自动排序,但是取用元素时好像是O(log n)
由于我对STL没有过多了解,就不误人子弟了
III 高级压行
III.I 巧用define
define
里可以塞一些“变量”,例如大家常用的#define max(a, b) ((a)>(b)? (a): (b))
,#define lowbit(x) ((x)&-(x))
##
是define
里的连接符:它可以将两个常量字符串或变量字符串连接。
于是我们可以写一个这样的define
句子:#define rep(i, a, b) for(int i=(a), i##up=(b); i<=i##up; ++i)
引用自https://www.luogu.com/article/rb9991an
但是,需要注意的是define
只是单纯的替换
如ans=max(fun1(),ans)
,他完全等价于ans=(fun1()>ans?fun1(),ans)
,这意味着fun1
函数将会执行两次,而不是先计算fun1()
的值,在进行比较,将ans置为其中计算好的较大的值。
所以,define虽好,但也不可以滥用,以免造成时间复杂度上升。
以下是一些我常用的宏定义:
#define _rep(i,st,n) for(int i=(st);i<(n);++i) #define rep(i,st,n) for(int i=(st);i<=(n);++i) #define _dwh(i,st,n) for(int i=(st);i>(n);--i) #define dwh(i,st,n) for(int i=(st);i>=(n);--i)
III.II 巧用lambda
表达式
上文中我们提到三元运算符中不能放代码块,但是lambda
表达式可以。
但他仍类似于函数(也要定义),且经格式化邪教后大括号会展开,所以更多应用于sort中,
例:
sort(a+1,a+1+n,[](const node& x,const node& y){return /* TODO */;});
关于它的使用请自行bdfs或这边粗略看一下,没时间讲太多。
捕获变量时建议隐式引用捕获,这样你可以不用写太多。
例子:
for(scanf("%d",&n);i<=n;i++) { scanf("%d%d%d",&opt,p+i,t+i); if(!opt) yh[++top]=p[i],sj[top]=t[i],ans+=p[i] else { func(); for(k=0,f=1,j=m;j<=top&&f;j++) r[j]?1:(t[i]-sj[j]>45?m=j:(yh[j]>=p[i]&&(k=j)&&(r[k]=true)?f=0:1)); if(!k) ans+=p[i]; } }
运用lambda
技巧后
auto func=[&](){for(k=0,f=1,j=m;j<=top&&f;j++) r[j]?1:(t[i]-sj[j]>45?m=j:(yh[j]>=p[i]&&(k=j)&&(r[k]=true)?f=0:1));}; for(scanf("%d",&n);i<=n;i++) scanf("%d%d%d",&opt,p+i,t+i),(!opt)?yh[++top]=p[i],sj[top]=t[i],ans+=p[i]:(func(),!k?ans+=p[i]:6);
当lambda
表达式塞在三元运算符中,由于三元运算符强制要求有返回值,且lambda
默认是void
,所以,你应:
XXXX?XXX:(func(),0);
或在定义时就明确指出返回值不是void
auto func=[]()->int{114514;}; //... XXXX?XXX:func();
IV. 其他技巧
(零零散散的,以后再整理吧)
IV.I 压输入输出
输入时可以压行到变量的定义中(我有时候压到for循环中)的,使用逗号运算符即可(cin cout输入本质上是一个类的重载运算符)
例:
int x,y=(cin>>x,0);
由于scanf,printf函数的参数读入是依赖第一个字符串所指代的内存。所以scanf("%d",&x,y=100);
是可以的
但是你得保证后续没有任何数据读入
因而方法有时会造成爆0,不建议使用
IV.II 使用工具
必备工具:压行机
注:有时候可能太卡进不去,所以建议Ctrl + S
先行保存
当真的压无可压的时候可使用(所以,这往往才是压行的最后一步)
IV.III 剩余的其他
- 主函数中int可省略,这是默认的。
return 0;
也可以省略。printf
返回的是输出长度(显然不会为0)所以是return!printf("%d",ans);
for
可等效代替while
,简短语句(把变量定义放进去时,一定要注意作用域的问题,循坏外就不能用这些变量了)
结语
最后,勤加练习,你的压行便会大成:
//本代码提交0分 #include<stdio.h> const int N=100010; int opt,n,ans,top,m=1,k,f; int t[N],p[N],yh[N],sj[N]; bool r[N]; int main() { int i=1,j; for(auto func=(scanf("%d",&n),[&](){for(k=0,f=1,j=m;j<=top&&f;j++) r[j]?1:(t[i]-sj[j]>45?m=j:(yh[j]>=p[i]&&(k=j)&&(r[k]=true)?f=0:1));});i<=n;i++) scanf("%d%d%d",&opt,p+i,t+i,(!opt)?yh[++top]=p[i],sj[top]=t[i],ans+=p[i]:(func(),!k?ans+=p[i]:6)); return!printf("%d",ans); }//为了代码的可读性,还未使用压行机
(如需转载,请标明出处)
本人实力不济,如有错误或其他建议及补充(包括但不限于压行技巧),欢迎指出。
参考资料
压行万岁
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)