浅谈宏定义——循环宏定义
题目:浅谈宏定义——循环宏定义
作者:\texttt{Eason\underline{~}AC}
0 前言及鸣谢
宏定义,大家都可能会听说,而且也会用,比如像以下的代码:
//代码0.1 数组逆序输出I
#include <bits/stdc++.h>
#define maxn 10005 //宏定义在这里哦!
using namespace std;
int a[maxn];
int main() {
int n;
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=n; i>=1; i--)
cout<<a[i]<<" ";
return 0;
}
这个代码中,#define maxn 10005就是一个最普通的宏定义,它将maxn赋值为了10005。不过,你可能不知道(重点来了!),这个程序里的for循环竟然也可以通过宏定义简化!接下来,我们开始进一步了解宏定义,等到所有讲解了结后,我们在对这个程序进行进一步的简化。
在开始讲解之前,我谨在此特别感谢陈锋dalao所编写的《算法竞赛入门经典——习题与解答》,是本书的第一章教会了我如何实现宏定义!(有兴趣的可以去买一下)
1 一个简单的例子
让我们先看一下这个题目:UVa1379(由于篇幅原因,不再对题目进行赘述,有兴趣的可以去做一下这道题) 这个题目的代码如下:
//代码1.1 UVA1379(未使用宏定义)
#include <bits/stdc++.h>
using namespace std;
const int MAXN=104,MAXG=200+10+4;
struct WinP {
int p,pit;
WinP():p(0),pit(0){}
bool operator<(const WinP& s) const {
return p>s.p;
}
};
WinP winps[MAXN][MAXN];
int N,M,numG,G[MAXG],DP[2][6][6][6][6];
inline int getPi(int op, int i) {
return winps[op][i].pit;
}
double solve() {
memset(DP,0,sizeof(DP));
int ans=0,cur=0;
for(int i=1; i<=numG; i++) {
int prev=cur,now=1-cur;
cur=1-cur;
memset(DP[now],0,sizeof(DP[now]));
int op=G[i];
if(op==0) {
for(int p1=0; p1<=5; p1++) {
for(int p2=0; p2<=5; p2++) {
for(int p3=0; p3<=5; p3++) {
for(int p4=0; p4<=5; p4++) {
int& d=DP[now][0][p1][p2][p3];
d=max(d,DP[prev][p1][p2][p3][p4]);
ans=max(d,ans);
}
}
}
}
continue;
}
for(int i0=1; i0<=5; i0++) {
int opi=getPi(op,i0);
for(int p1=0; p1<=5; p1++) {
if(i>1 && getPi(G[i-1],p1)==opi) continue;
for(int p2=0; p2<=5; p2++) {
if(i>2 && getPi(G[i-2],p2)==opi) continue;
for(int p3=0; p3<=5; p3++) {
if(i>3 && getPi(G[i-3],p3)==opi) continue;
for(int p4=0; p4<=5; p4++) {
if(i>4 && getPi(G[i-4],p4)==opi) continue;
int& d=DP[now][i0][p1][p2][p3];
d=max(d,DP[prev][p1][p2][p3][p4]+winps[op][i0].p);
ans=max(d,ans);
}
}
}
}
}
}
double d=ans*.01;
return d;
}
int main() {
int T; scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&N,&M,&numG);
numG+=10;
for(int i=1; i<=M; i++) {
for(int j=1; j<=N; j++) {
scanf("%d",&(winps[i][j].p));
winps[i][j].pit=j;
}
sort(winps[i]+1,winps[i]+N+1);
}
G[0]=0;
for(int i=1; i<=numG; i++)
scanf("%d",&(G[i]));
double ans=solve();
printf("%.2lf\n",ans);
}
return 0;
}
是不是很长,而且看上去很烦人?
接下来,我来教教大家怎么使用宏定义简化这个程序中的for循环(敲黑板):
在算法比赛中,写的最多的代码就是像这样的代码:
#define _for(i,a,b) for(int i=(a); i<(b); i++)
#define _rep(i,a,b) for(int i=(a); i<=(b); i++)
//注意a和b一定要打括号!
这样写循环,就会将上述for循环简化成:_for(i,0,N),这里的a,b两个参数都可以传入表达式。宏使用恰当,可以大量简化代码,例如:
vector b;
_for(i,1,a.size()) {...}
有了宏定义,UVA1379的代码可简化成:
//代码1.2 UVA1379(使用宏定义)
#include <bits/stdc++.h>
#define _for(i,a,b) for(int i=(a); i<(b); i++)
#define _rep(i,a,b) for(int i=(a); i<=(b); i++)
using namespace std;
const int MAXN=104,MAXG=200+10+4;
struct WinP {
int p,pit;
WinP():p(0),pit(0){}
bool operator<(const WinP& s) const {
return p>s.p;
}
};
WinP winps[MAXN][MAXN];
int N,M,numG,G[MAXG],DP[2][6][6][6][6];
inline int getPi(int op, int i) {
return winps[op][i].pit;
}
double solve() {
memset(DP,0,sizeof(DP));
int ans=0,cur=0;
_rep(i,1,numG) {
int prev=cur,now=1-cur;
cur=1-cur;
memset(DP[now],0,sizeof(DP[now]));
int op=G[i];
if(op==0) {
_rep(p1,0,5) _rep(p2,0,5) _rep(p3,0,5) _rep(p4,0,5){
int& d=DP[now][0][p1][p2][p3];
d=max(d,DP[prev][p1][p2][p3][p4]);
ans=max(d,ans);
}
}
}
}
continue;
}
_rep(i0,1,5) {
int opi=getPi(op,i0);
_rep(p1,0,5){
if(i>1 && getPi(G[i-1],p1)==opi) continue;
_rep(p2,0,5){
if(i>2 && getPi(G[i-2],p2)==opi) continue;
_rep(p3,0,5) {
if(i>3 && getPi(G[i-3],p3)==opi) continue;
_rep(p4,0,5) {
if(i>4 && getPi(G[i-4],p4)==opi) continue;
int& d=DP[now][i0][p1][p2][p3];
d=max(d,DP[prev][p1][p2][p3][p4]+winps[op][i0].p);
ans=max(d,ans);
}
}
}
}
}
}
double d=ans*.01;
return d;
}
int main() {
int T; scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&N,&M,&numG);
numG+=10;
_rep(i,1,M) {
_rep(j,1,N) {
scanf("%d",&(winps[i][j].p));
winps[i][j].pit=j;
}
sort(winps[i]+1,winps[i]+N+1);
}
G[0]=0;
_rep(i,1,numG)
scanf("%d",&(G[i]));
double ans=solve();
printf("%.2lf\n",ans);
}
return 0;
}
咋样,这程序看上去简洁多了吧? 接下来,咱们再看一个例子,看看宏定义怎样使程序简洁——
2 宏定义的应用
洛谷P1428 小鱼比可爱
这个题目的源代码如下:
//代码2.1 小鱼比可爱(未使用宏定义)
#include <bits/stdc++.h>
using namespace std;
const int MAXN=10000;
int a[MAXN],b[MAXN],n;
int main() {
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1; i<=n; i++) {
for(int j=i-1; j>0; j--) {
if(a[j]<a[i])
b[i]++;
}
}
for(int i=1; i<=n; i++)
cout<<b[i]<<" ";
return 0;
}
怎样,看上去,很多for循环,烦人吧?
那么咱们开始用吸星大法了:
//代码2.2 小鱼比可爱(使用宏定义)
#include <bits/stdc++.h>
#define _rep(i,a,b) for(int i=(a); i<=(b); i++)
#define _les(i,a,b) for(int i=(a); i>(b); i--)//重中之重!
using namespace std;
const int MAXN=10000;
int a[MAXN],b[MAXN],n;
int main() {
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
_rep(i,1,n)
_les(j,i-1,0)
if(a[j]<a[i])
b[i]++;
_rep(i,1,n)
cout<<b[i]<<" ";
return 0;
}
是不是看上去很舒服呢?
那接下来,咱们再看到代码0.1,学完宏定义,是不是可以简化这个程序呢?那咱们来开始给它整整容:
//代码2.3 数组逆序输出II
#include <bits/stdc++.h>
#define maxn 10005
#define _repup(i,a,b) for(int i=(a); i<=(b); i++)
#define _reples(i,a,b) for(int i=(a); i>=(b); i--)
using namespace std;
int a[maxn];
int main() {
int n;
cin>>n;
_repup(i,1,n)
cin>>a[i];
_reples(i,n,1)
cout<<a[i]<<" ";
return 0;
}
3 一些警告
1.本文中所有程序均为AC代码,若抄袭,屎名后果自负。
2.本文有些语言采自陈锋的《算法经典入门经典——习题与解答》,但非抄袭,若有疑问,可私信给我(本蒟蒻UID:112917)。
4 结尾
那好了,本次宏定义的讲解到此结束,希望各位OIer们在解题时,多多简化代码,这样,别人看得懂,自己也不会晕。加油哦!QAQ233
2018.9.29于长沙市麓山国际实验学校机房写