ACM題目中關於數據的生成以及OJ上Linux和Windows裏回車和換行的處理

ACM題目中關於數據的生成以及OJ上Linux和Windows裏回車和換行的處理

前言:都是一些淺層的理解,求各位大佬輕噴。

基本的隨機數生成

一個好的題目需要有強大的數據來支撐,數據一般由兩部分組成,一是邊界數據,二是隨機數據。對於前者,可能需要手寫數據來生成,而後者可以藉助rand()函數一次性生成多組隨機數據。

因爲rand()函數的內部是利用線性同餘法實現的,週期較長導致隨機性並不強,所以需要在main開始時藉助srand()函數設置隨機數種子,增強隨機性。

代碼:

srand(time(0));

對於整數的生成,我們可以藉助rand()函數和%來生成固定範圍的整數。比如,我想要獲得[a,b)內的隨機整數,就可以

int tmp=rand()%(b-a)+a;

其實寫法有很多種,具體參考:傳送門

爲了提高生成隨機數的隨機性,我們可以加一點小優化(maybe有用)

int m=(rand()*rand()+rand())%1000+1;

這時候要注意,如果有題目要求一些數不能重複,比如山理工數據結構實驗的選課名單,生成數據的時候就需要用map加一個判重的環節。

map<int,bool>mp;
for(int j=1;j<=cnt;j++){
	int x=(rand()*rand()+rand())%m+1;
	if(mp1[x]){
		j--;
		continue;
	}
	mp1[x]=1;
	cout<<x<<" ";
}

對於隨機字母的生成,我們只需要在生成隨機數後將隨機數轉化爲字母,代碼如下:(全是小寫字母的情況)

string name;
int x=(rand()*rand()+rand())%26;
name+=(x+'a');

如果想要不區分大小寫的話,可以在增設一個rand()生成固定區間的數,根據區間中點來判斷是生成大寫還是小寫字母。其他方法可以自行尋找~

隨機生成多個文件

懂得了以上的隨機數生成法則,我們就可以愉快的造數據了。一般OJ的判題規則是在後臺有多組數據,每組數據有兩種文件,in文件表示該組數據的輸入,out文件表示該組數據的輸出,後臺會運行你的代碼並且將in文件的運行結果與out文件對比從而得到AC,WA,PE等多種結果。

生成數據的時候我們要做的就是一次性生成多個對應的in與out文件,上傳服務器。

直接就給出學長的代碼了,註釋已加。

生成數據的題的題面:(好繞口啊) http://icpc.ldu.edu.cn/acm/problem.php?id=2871

大家可以先閱讀題目和所給的要求再來結合代碼理解(其實主要就是套板子)

#include<bits/stdc++.h>
using namespace std;
#define PI 3.1415926
typedef long long ll;
const int maxn=1e6+7;
const ll mod=1e16;
map<string,bool>mp;///判斷名字是否有重複 
map<int,bool>mp1;///判斷選課的序號是否有重複 
int main() {
	srand(time(0));//隨機數種子 
	for(int qi=1; qi<=15; qi++) {///想生成數據的組數 
	///以下是修改生成文件的名字 
		char str[120]= {"2871"};//可增加名字,最好爲英文,中文容易亂碼 
		int len=strlen(str);
		int temp=qi;
		char save[50000];
		int q=0;
		while(temp) {
			save[q++]=(char)((temp%10)+'0');
			temp/=10;
		}
		for(int i=q-1; i>=0; i--)
			str[len++]=save[i];
		str[len]='\0';
		strcat(str,".in");
		freopen(str,"w",stdout);
//------------------------------------------------------------------------------------------------ 
		///in start
		//(替換爲想生成的) 
		//以下是生成in文件的數據,生成數據後輸出,因爲前面加了文件操作所以會輸出到文件裏 
		///	int m=(rand()*rand()+rand())%1000+1;
		int n=(rand()*rand()+rand())%350+1,m=(rand()*rand()+rand())%100+1;//生成學生總數和課程數量 
		mp.clear();//清空學生名字的map 
		cout<<n<<" "<<m<<endl;//將學生總數和課程數量輸出到文件 
		for(int i=1;i<=n;i++){///生成每一個學生對應的信息 
			mp1.clear();///清空每個學生的選課列表 
			///生成名字 
			string name;
			int namelen=(rand()*rand()+rand())%6+2;///名字中字母的長度,爲了不超長度所以設的小了一點,個人習慣 
			for(int j=0;j<namelen;j++){
				int x=(rand()*rand()+rand())%26; 
				name+=(x+'a');//可以優化爲生成大寫或小寫字母 
			}
			for(int j=0;j<2;j++){//生成名字裏的學號 
				int x=(rand()*rand()+rand())%10;
				name+=(x+'0');//轉成字符串 
			}
			///去重名字 
			while(mp[name]){
				name="";
				namelen=(rand()*rand()+rand())%6+2;
				for(int j=0;j<namelen;j++){
					int x=(rand()*rand()+rand())%26;
					name+=(x+'a');
				}
				for(int j=0;j<2;j++){
					int x=(rand()*rand()+rand())%10;
					name+=(x+'0');
				}
			}
			mp[name]=1;//標記名字 
			cout<<name<<" ";//將名字輸出到文件裏 
		
			int cnt=(rand()*rand()+rand())%m+1;	///生成該學生的選課數量
			cout<<cnt<<" ";//將該學生的選課數量輸出到文件裏 
			for(int j=1;j<=cnt;j++){ 
				int x=(rand()*rand()+rand())%m+1;//生成該學生選課的序號 
				if(mp1[x]){//判斷之前是否已經出現過,如果出現則該序號不算數 
					j--;
					continue;
				}
				mp1[x]=1;//標記 
				cout<<x<<" ";//將選課序號輸出到文件 
			}
			puts("");//輸出回車 
		}
//------------------------------------------------------------------------------------------------ 
		///in end
		//以下爲文件讀寫操作 
		fclose(stdout);
		len=strlen(str);
		str[len-2]='\0';
		strcat(str,"out");
		freopen(str,"w",stdout);
		///out start
		///可以加標程什麼的 
        ///out end
		fclose(stdout);
	}
	return 0;
}

以上是in文件的數據的生成,對於out文件的數據生成,可以在上述代碼裏的標程部分添加對應的標程,也可以在數據所在的文件夾裏新建cpp文件並將標程放入,在main開頭加入:

freopen("1.in","r",stdin);
freopen("1.out","w",stdout);

表示從文件1.in裏讀取並且輸出到1.out文件裏。

兩種方法各有利弊,看喜好選擇啦就。

到這裏基本的數據生成已經結束了,大家可以自己出完整的題目啦!

接下來說一下我鼓搗的一下午的事情。

對於有些字符或字符串的題目,可能有時候玄學的標程都會不過,這是因爲大部分OJ是Linux系統,而生成數據的時候用的Windows系統,而 windows中的換行符是\r\n, linux/unix下的換行符是\n。 具體的詳細的解釋可以看博客

我們要做的就是將Windows的換行符轉化爲Linux格式的。

這個問題大體可以分爲三步,大前提是你要有Linux系統,可以裝一個VMware虛擬機,具體的安裝教程就不說了(實際上是我不會)

1.將想要更改格式的文件導入虛擬機

2.在虛擬機內完成格式的轉化

3.將更改完後的文件導出虛擬機

對於1.3步,可以藉助VMware的VMware Tools完成,也可以直接在虛擬機裏登入服務器,在虛擬機裏進行格式的轉化後再將文件上傳至服務器。

關鍵是第二步,有很多方法,這裏介紹一種比較好用的,其他方法可參考網上博客

將想要轉化的文件所在的文件夾在終端中打開,輸入 dos2unix ,後面加文件名,可以是多個。我的虛擬機把語言換了,默認是英文的。

[root@localhost-live 2860]# dos2unix 1.in 2.in 3.in 4.in 5.in 6.in 7.in 8.in 9.in 10.in 11.in 12.in 13.in 14.in 15.in 16.in 17.in 18.in 19.in 20.in
dos2unix: 正在轉換文件 1.in 爲Unix格式...
dos2unix: 正在轉換文件 2.in 爲Unix格式...
dos2unix: 正在轉換文件 3.in 爲Unix格式...
dos2unix: 正在轉換文件 4.in 爲Unix格式...
dos2unix: 正在轉換文件 5.in 爲Unix格式...
dos2unix: 正在轉換文件 6.in 爲Unix格式...
dos2unix: 正在轉換文件 7.in 爲Unix格式...
dos2unix: 正在轉換文件 8.in 爲Unix格式...
dos2unix: 正在轉換文件 9.in 爲Unix格式...
dos2unix: 正在轉換文件 10.in 爲Unix格式...
dos2unix: 正在轉換文件 11.in 爲Unix格式...
dos2unix: 正在轉換文件 12.in 爲Unix格式...
dos2unix: 正在轉換文件 13.in 爲Unix格式...
dos2unix: 正在轉換文件 14.in 爲Unix格式...
dos2unix: 正在轉換文件 15.in 爲Unix格式...
dos2unix: 正在轉換文件 16.in 爲Unix格式...
dos2unix: 正在轉換文件 17.in 爲Unix格式...
dos2unix: 正在轉換文件 18.in 爲Unix格式...
dos2unix: 正在轉換文件 19.in 爲Unix格式...
dos2unix: 正在轉換文件 20.in 爲Unix格式...

然後再說說如何將“下載”這個文件夾在終端打開。因爲終端只能輸入英文字符(maybe),我們右擊就會出現該選項。

在這裏插入圖片描述
這個問題還是很煩人的0.0.
結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章