今天讓咱們整理一下一些常數優化技巧:c++
1. 讀入優化:數組
#define sight(c) ('0'<=c&&c<='9') inline void read(int &x){ static char c; for (c=getchar();!sight(c);c=getchar()); for (x=0;sight(c);c=getchar())x=x*10+c-48; }
//使用方法 read(x);
這是一直基於getchar的快速讀入。相比你們都會,不說了。函數
2.更快的讀入優化:測試
#define getchar nc #define sight(c) ('0'<=c&&c<='9') inline char nc(){ static char buf[1000000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++; } inline void read(int &x){ static char c; for (c=getchar();!sight(c);c=getchar()); for (x=0;sight(c);c=getchar())x=x*10+c-48; }
咱們用buf數組把全部的輸入都讀入到buf數組裏,還要快。(此後便不能用scanf和cin了,由於輸入在buf數組裏了)優化
3.若是咱們大抵知道數據輸入規模,咱們能夠這樣寫nc函數:this
#define nc *p1++ char *p1=buf fread(stdin,buf,1,100000);//主程序里加這句話
4.同理,咱們有輸出優化:spa
void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); }
要用的時候調用writeln。(在寫write函數的時候要少寫判斷。)操作系統
5.不要信仰STL的常數(sort除外)。調試
#define min(a,b) (a)<(b)?(a):(b) #define max(a,b) (a)>(b)?(a):(b) #define sight(c) ('0'<=c&&c<='9') #define swap(a,b) a^=b,b^=a,a^=b
必要的時候必定要手寫(尤爲是bitset,queue,stack)。code
such as bitset。
struct bitsets{ long long t[4]; void set(int x){ int aa,bb; aa=x/60;bb=x%60; this->t[aa]|=1LL<<bb; } void reset(){ this->t[0]=0; this->t[1]=0; this->t[2]=0; this->t[3]=0; } void ad(const bitsets &g){ this->t[0]&=g.t[0]; this->t[1]&=g.t[1]; this->t[2]&=g.t[2]; this->t[3]&=g.t[3]; } void oo(const bitsets &g){ this->t[0]|=g.t[0]; this->t[1]|=g.t[1]; this->t[2]|=g.t[2]; this->t[3]|=g.t[3]; } void xo(const bitsets &g){ this->t[0]^=g.t[0]; this->t[1]^=g.t[1]; this->t[2]^=g.t[2]; this->t[3]^=g.t[3]; } bool tr(const bitsets &g){ bool top=true; top&=((this->t[0]&g.t[0])==0); top&=((this->t[1]&g.t[1])==0); top&=((this->t[2]&g.t[2])==0); top&=((this->t[3]&g.t[3])==0); return top; } };
6.大規模的函數參數調用最好引用(&)
好比這樣:
void X(int &x){ }
7.咱們要學會手開O2
#define MARICLE __attribute__((optimize("-O2")))
那麼咱們就能夠在要開O2的函數過程前加 MARICLE 。
或者在宏中定義如下宏
#pragma GCC optimize("-O2")
8.Ox優化並非越高越好:
咱們發現Ox類的優化是有代價的。咱們發現開着O類優化咱們沒法調試。
-O1 提供基礎級別的優化
-O2提供更加高級的代碼優化,會佔用更長的編譯時間
-O3提供最高級的代碼優化
此外咱們發現O3優化是以縮小棧空間爲代價的,因此有遞歸的程序O2優化就夠了,O3會更慢。
9.循環優化:
for (i=1;i<=b;i++)
這樣寫是很慢的。
for (int i=1;i<=b;i++)
這樣寫要快那麼一丟丟,由於在循環體裏面定義,編譯器會把變量放到寄存器裏,這樣會更快。
for (int i=b;i;i--) //i從b到1 for (int i=b;~i;i--)//i從b到0
這樣子更快。由於是位運算。
10.枚舉子集。
for (int i=0;i<siz;i++) for (int j=i;j;j=(j-1)&i) f[i]=.....
這樣子寫是O(3^n)的,比常規枚舉要快。
11.相同語句下,從速度上講,宏>重載運算符>函數。
12.沒有遞歸的程序前能夠加inline,這樣更快。
inline void X(int &x){ }
就是這樣子。
13.咱們能夠這樣子用位運算來加速邏輯判斷:
if (a^b) 等價於 if (a!=b) if (!(a^b)) 等價於 if (a==b)
最後聲明,其實在Os優化中,這些優化差很少都有,其實然並軟。
好吧,我知道我寫的很沒有養分。仍是舉個栗子吧。
{如下測試都在一臺配置以下的機子中:
CPU: intel i3-6100 3.7GHZ
RAM: 4.00GB
64 位操做系統
}
隨便寫了個程序 :
#include<bits/stdc++.h> using namespace std; int n,a[300007],ans; int gcd(int x,int y){ return y?gcd(y,x%y):x; } signed main () { freopen("a.in","r",stdin); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) ans=max(ans,gcd(a[i],a[j])); printf("%d\n",ans); }
而後造了一個2W數據規模的數據。 運行了38.85s.
在其上方加入如下指令:
#pragma GCC optimize("-O2") 28.48s
在編譯器中加入O2優化指令: 28.22s
//如下操做都在O2的基礎上
在gcd函數前加入inline 指令 28.5s (大概是系統忽視了inline ,由於遞歸的函數加inline講道理會很假)
咱們強制內聯 :加入 __attribute__((always_inline)) 28.49s。
把遞歸改爲迭代:
#pragma GCC optimize("-O2") #include<bits/stdc++.h> using namespace std; int n,a[300007],ans,t; int gcd(int x,int y){ while (y) { t=x; x=y; y=t%x; } return x; } signed main () { freopen("a.in","r",stdin); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) ans=max(ans,gcd(a[i],a[j])); printf("%d\n",ans); }
結果並無快多少,應該是O2自動把gcd展開了吧。
#pragma GCC optimize("-O2") #include<bits/stdc++.h> using namespace std; int n,a[300007],ans,t; #define getchar nc #define sight(x) ('0'<=x&&x<='9') inline char nc(){ static char buf[1000000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++; } inline void read(int &x){ static char c; for (c=getchar();!sight(c);c=getchar()); for (x=0;sight(c);c=getchar())x=x*10+c-48; } void write(int x){if (x<10) {putchar('0'+x); return;} write(x/10); putchar('0'+x%10);} inline void writeln(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar('\n'); } inline void writel(int x){ if (x<0) putchar('-'),x*=-1; write(x); putchar(' '); } inline int gcd(int x,int y){ while (y) { t=x; x=y; y=t%x; } return x; } int p[13],ed,c[300007],pppp; signed main () { freopen("a.in","r",stdin); read(n); for (int i=1;i<=n;i++) read(a[i]); for (int i=n;i;--i) { ed=8; for (int j=1;ed<i;j+=8,ed+=8){ p[0]=gcd(a[i-j],a[j]); c[j]>p[0]?:c[j]=p[0]; p[1]=gcd(a[i-j-1],a[j+1]); c[j+1]>p[1]?:c[j+1]=p[1]; p[2]=gcd(a[i-j-2],a[j+2]); c[j+2]>p[2]?:c[j+2]=p[2]; p[3]=gcd(a[i-j-3],a[j+3]); c[j+3]>p[3]?:c[j+3]=p[3]; p[4]=gcd(a[i-j-4],a[j+4]); c[j+4]>p[4]?:c[j+4]=p[4]; p[5]=gcd(a[i-j-5],a[j+5]); c[j+5]>p[5]?:c[j+5]=p[5]; p[6]=gcd(a[i-j-6],a[j+6]); c[j+6]>p[6]?:c[j+6]=p[6]; p[7]=gcd(a[i-j-7],a[j+7]); c[j+7]>p[7]?:c[j+7]=p[7]; } for (int j=ed-7;j<i;++j){ pppp=gcd(a[i],a[j]); c[j]>pppp?:c[j]=pppp;} } for (int i=n;i;--i) ans>c[i]?:ans=c[i]; printf("%d\n",ans); }
循環展開,但不知道爲何,並無快。
應該是gcd太大了吧,反正循環展開就是這樣的。