NOIP比賽中如何加速c++的輸入輸出

#NOIP比賽中如何加速c++的輸入輸出

由於博客遷移的原因,本博客已經遷移到此鏈接,並修改了原博客中的一些錯誤,歡迎大家訪問

在競賽中,遇到大數據時,往往需要更快的讀取方式。由於比賽中輸出一般規模較小,本文只討論輸入如何加速.
現在我們生成1000000個隨機數,構成1000*1000的矩陣,然後輸入比較時間(Win 10系統)

#include<iostream>
#include<stdlib.h>
#include<ctime>
using namespace std;
int main(){
	srand((unsigned)time(NULL));
	freopen("out1.txt","w",stdout);
	for(int i=1;i<=1000;i++){
		for(int j=1;j<=1000;j++){
			cout<<rand()<<' ';
		}
		cout<<endl;
	}
}

##cin的速度

在比賽中,經常出現數據集超大造成 cin TLE的情況。這時候大部分人(包括原來我也是)認爲這是cin的效率不及scanf的錯

準確的說,cin在不優化的情況下效率是很低的,我們來測試一下

#include<iostream>
#include<cstdio>
#include<ctime>
using namespace std;
int a[1005][1005];
int main(){
	freopen("out1.txt","r",stdin);
	double s=clock();
	for(int i=1;i<=1000;i++){
		for(int j=1;j<=1000;j++){
			cin>>a[i][j];
		}
	}
	printf("time used=%.3fs\n",double(clock()-s)/CLOCKS_PER_SEC);
}

cin的速度
可以看出,cin的用時達到了驚人的0.763s!!!假如運行時間限制爲1s,那麼程序只剩下0.3秒來計算,是極容易TLE的
因此,遇到大數據時儘量避免用cin
有博客提到:

默認的時候,cin與stdin總是保持同步的,也就是說這兩種方法可以混用,而不必擔心文件指針混亂,同時cout和stdout也一樣,兩者混用不會輸出順序錯亂。正因爲這個兼容性的特性,導致cin有許多額外的開銷,如何禁用這個特性呢?只需一個語句std::ios::sync_with_stdio(false);,這樣就可以取消cin與stdin的同步了

其實還有一個等價的寫法

cin.tie(0);//取消cin的同步
cout.tie(0);//取消cout的同步

我們來驗證一下:

#include<iostream>
#include<cstdio>
#include<ctime>
using namespace std;
int a[1005][1005];
int main(){
	freopen("out1.txt","r",stdin);
	std::ios::sync_with_stdio(false);//優化
	double s=clock();
	for(int i=1;i<=1000;i++){
		for(int j=1;j<=1000;j++){
			cin>>a[i][j];
		}
	}
	printf("time used=%.3fs\n",double(clock()-s)/CLOCKS_PER_SEC);
}

cin的優化
時間變成了0.173s,相比cin是飛躍性的優化
但是別急着高興,本人親測,在NOIP的評測機上這樣子會爆0
因此,noip比賽中堅決不要寫std::ios::sync_with_stdio(false)
爆0的原因如下
noip明確要求使用freopen,而freopen是stdio庫中的,既然我們已經取消了iostream和stdio的同步,這樣會造成文件指針混亂,進而導致RE

##scanf的速度
既然noip比賽中堅決不要寫std::ios::sync_with_stdio(false),那麼我們可以用scanf

#include<iostream>
#include<cstdio>
#include<ctime>
using namespace std;
int a[1005][1005];
int main(){
	freopen("out1.txt","r",stdin);
	double s=clock();
	for(int i=1;i<=1000;i++){
		for(int j=1;j<=1000;j++){
			scanf("%d",&a[i][j]);
		}
	}
	printf("time used=%.3fs\n",double(clock()-s)/CLOCKS_PER_SEC);
}

這裏寫圖片描述
時間變成了0.641s,相比無優化的cin還是較快的

##手寫快速讀入的速度
我們知道,getchar的速度是很快的,但它只能讀取單個字符,因此,我們通過將字符轉爲整型來優化,同理可以轉成long long
快速讀入的代碼NOIP初賽曾經考過

#include<iostream>
#include<cstdio>
#include<ctime>
using namespace std;
int a[1005][1005];
inline int read(){//非常重要的快速讀入代碼
	int x=0,sign=1;
	char c=getchar();
	while(c>'9'||c<'0'){//判斷符號
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){//轉換數
		x=x*10+c-'0';
		c=getchar();
	}
	return x*sign;
}
int main(){
	freopen("out1.txt","r",stdin);
	double s=clock();
	for(int i=1;i<=1000;i++){
		for(int j=1;j<=1000;j++){
			a[i][j]=read();
		}
	}
	printf("time used=%.3fs\n",double(clock()-s)/CLOCKS_PER_SEC);
}

運行後的結果如下:
這裏寫圖片描述
現在這個方法只需0.163s,比其他的方法都快,而且不會在評測機上出現問題,並且也沒有調用許多函數
遇到數據量大的題,儘量用手寫的快速讀入來讀取

##總結

  1. 遇到大數據時儘量避免用cin
  2. noip比賽中堅決不要寫std::ios::sync_with_stdio(false)來優化cin
  3. 如果是double或輸入格式較複雜用scanf
  4. 遇到數據量大的題,且是long long或int,儘量用手寫的快速讀入來讀取
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章