1. 程式人生 > >【提高組NOIP2017】時間複雜度 題解 分治系統處理

【提高組NOIP2017】時間複雜度 題解 分治系統處理

原題邊幅很長,這裡就不貼出來了,落谷有原題,不清楚的可以去看看

------------------------------------------------------------------------------------------------------------

這是一道大模擬,我的方法是建立輸入、編譯和判斷三大系統。

 

這三大系統用三個函式實現,分別是

void Input() //輸入系統,專門讀取資料並把字串內的有價值資料提取出來

bool complite() //編譯系統,對整組資料進行語法檢查,返回true代表語法正確

bool work() //判斷系統,符合時間複雜度返回

true,否則返回false

 -------------------------------------------------------------------------------------------------------------------

主要流程:

 

所以主函式是這樣的:

int main()
{
	cin>>t;
	for(int i=0; i<t ;i++){ //迴圈t次,每組輸入一組資料
	Init(); //該函式的作用為初始化所有陣列與全域性變數
	Input(); //讀入資料
/*編譯成功就對資料進行判斷並輸出”Yes”或”No”,否則輸出”ERR”*/
	if(complite()){
		if(work()) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	else cout<<"ERR"<<endl;
	}
	return 0;
}


 ----------------------------------------------------------------------------------------------------------------------------

那麼接下來的任務就很明確了,就是一個個實現三大系統

我們讀到的資料是字串,字串是不方便直接處理的,所以需要輸入系統的資料提取功能,把字串的有用的同類型資料轉化成一個個陣列存取,具體怎麼提取,還要看接下來編譯與判斷系統需要的是字串中的哪些資訊。在本題目中,我把一行字串看成由以下四類資料組成。

 

字串開頭非FEO(n^w)這行不需要進行編譯和判斷,只需要把

w提取出來就行了。

stringF型(即開頭為FE型以此類推)時才會有i x y

瞭解了字串的資料組成後,那麼資料的提取也就變得很簡單了:

開出這下面四個陣列

1. 用於記錄第i行的字串型別的陣列type[]

2. 用於記錄第i行的計數變數名的陣列ch[]

3. 用於記錄第i行的計數變數的值的陣列x[]

4. 用於記錄第i行的迴圈邊界值的陣列y[]

 -------------------------------------------------------------------------------------------------------------------------

編譯系統用到了其中的type[]ch[]陣列,看看其工作原理吧:

 

對應程式碼:

bool complite()
{
/*先檢查變數名,看有沒有毛病*/
	for(int i=1; i<n ;i++){
		if(type[i] == _F){
			if(var[ch[i]]) return false; //重複定義了變數
			else{
				var[ch[i]] = true;//建立變數
				Steak[++top] = ch[i]-'a';//儲存該層迴圈的變數
			}
		}
		else if(type[i] == _E){
			if(top >= 0)
				var[Steak[top--]] = false;//退出迴圈,銷燬該層迴圈變數
		}
	}
/*檢查F與E是否都匹配上*/
	int steak = 0;//小F棧
	for(int i=1; i<n ;i++){
		if(type[i] == _F) steak++;
		if(type[i] == _E){
			if(steak <= 0) return false; //E多了出來
			steak--;
		}
	}
	if(steak > 0) return false; //有尚未匹配的F
	return true;//無語法錯誤,返回真
}


------------------------------------------------------------------------------------------------------------------------------------

而判斷系統用到了type[]x[],  y[],由於流程較長,就用虛擬碼粗略講下,稍後會給全部程式

能進入判斷系統,說明所有語句都是合法的,每一個F都會有與其匹配的E

對於計算迴圈體的時間複雜度,我們可以採用遞迴的方式

Int pos2; //全域性變數,代表當前正在處理的行數
Int Jisuan(int pos) //pos表示計算的是第pos行迴圈的的時間複雜度
{
	pos2 = pos+1;
	If(第pos2行的字串型別為E型){
		pos2++;
		根據第pos行的x與y的關係返回0或1;
	}
 
	If(第pos行迴圈並沒有進入){
		跳過第pos行迴圈包含的所有迴圈並返回0;
	}
	Else{
		由第pos行的x和y關係算出該層迴圈本身固有的時間複雜度為1或n,並儲存在now中
		迴圈(只要第pos2行不是E型字串){
			Max = max(jisuan(各個子迴圈))
		}
	}
 
	now += Max;
	return now;
}


計算出迴圈體的時間複雜度後,可用來與題目標出來的時間複雜度向比較看是否匹配

Bool work()
{
	int Max; //最大時間複雜度迴圈體的時間複雜度
	迴圈(i 1:n){
		If(第i行字串為F型別){
			Max = max(Max, jisuan(i));
			i = pos2;
		}
	}
 
	If(Max == 規定時間複雜度) return true;
	Else return false;
}
 


這就是判斷系統的原理了。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

那麼剩下來的就是資料的提取了,也就是輸入系統的部分,我採用了getline()的方式去讀取,事實上從字串中提取資料的方法多種多樣,我的並不一定是最好的,這個大家可以到網上去查大神的操作,不過這裡我還是得貼出全部程式碼,不然以上講的程式也不成整體。

-------------------------------------------------------------------------------------------------------------------

全程式碼:

#include <iostream>
#include <string>
#include <cstring>
#define MAX 10000
#define N 9999999
using namespace std;

enum Type{
	_F,
	_E
};
string data[MAX];//輸入的資料 
/*設第i行的語句為 "F i x y" 則以下全域性變數的作用*/
int type[MAX]; //若type[i] = _F ,說明該行是以F開頭的語句 
int ch[MAX]; //用於記錄第i行的計數變數名的陣列
int x[MAX]; //用於記錄第i行的計數變數的值的陣列
int y[MAX]; //用於記錄第i行的迴圈邊界值的陣列
/************************************************/
bool var[30];//變量表,0~25對應a~z,true表示變數已建立 
int Steak[MAX];//F棧,用於編譯系統,方便銷燬變數 
int top = -1; //棧頂指標  

int time; //記錄輸入資料中要求的時間複雜度 

int pos2; //正在處理的行數,用於判斷系統 

int t;//共有t組資料 
int n;//該組資料有n行(實際上包括O(n^w)這行有n+1行) 

void Init()
{
	memset(var, 0, sizeof(var));
	memset(type, 0, sizeof(type));
	memset(x, 0, sizeof(x));
	memset(y, 0, sizeof(y));
	memset(Steak, 0, sizeof(Steak));
	memset(ch, 0, sizeof(ch));
	time = 0;
	pos2 = 0;
} 

void Input()
{
	int temp = 0;
	int pos;
	cin>>n;
	n++;
	for(int i=0; i<n ;i++)
	{
		getline(cin, data[i]);
		
		if(data[i][1] == 'O'){
			for(int j=4; j<data[i].size() ;j++)
				if(data[i][j]<='9' && data[i][j] >= '0')
					temp = temp*10 + data[i][j]-'0';
					
			time = temp;
		}
		else if(data[i][0] == 'F'){
			type[i] = _F;
			
			ch[i] = data[i][2];
			
			pos = 4;
			temp = 0;
			while(data[i][pos] != ' '){
				if(data[i][pos] == 'n'){
					x[i] = N;
				}
				else{
					temp = temp*10 + data[i][pos]-'0';
				}
				
				pos++;
			}
			
			if(x[i] != N) x[i] = temp;
			
			temp = 0;
			
			pos++;
			
			while(pos < data[i].size()){
				if(data[i][pos] == 'n'){
					y[i] = N;
				}
				else{
					temp = temp*10 + data[i][pos]-'0';
				}
				
				pos++;
			}
			
			if(y[i] != N) y[i] = temp;
		}
		else if(data[i][0] == 'E'){
			type[i] = _E;
		}
	}	
}

bool complite()
{
	/*先檢查變數名,看有沒有毛病*/
	for(int i=1; i<n ;i++){
		if(type[i] == _F){
			if(var[ch[i]]) return false; //重複定義了變數 
			else{
				var[ch[i]] = true;//建立變數 
				Steak[++top] = ch[i]-'a';//儲存該層迴圈的變數 
			}
		}
		else if(type[i] == _E){
			if(top >= 0)
				var[Steak[top--]] = false;//退出迴圈,銷燬該層迴圈變數 
		}
			
	}
	
	/*檢查F與E是否都匹配上*/
	int steak = 0;//小F棧 
	for(int i=1; i<n ;i++){
		if(type[i] == _F) steak++;
		if(type[i] == _E){
			if(steak <= 0) return false; //E多了出來 
			
			steak--;
		}
	}
	if(steak > 0) return false; //有尚未匹配的F 
	
	return true;//無語法錯誤,返回真 
}

int jisuan(int pos)
{
	if(type[++pos2] == _E){
		if(x[pos] != N && y[pos] == N){
			pos2++;
			return 1;	
		}
		else{
			pos2++;
			return 0;
		}
	}
	int now = 0;
	int steak = 1;
	if(x[pos] > y[pos]){ //沒進入迴圈 ,跳過所有迴圈並返回0 
		while(steak != 0){
			if(type[pos2++] == _E) steak--;
			else steak++;
		}
		
		return 0;
	}
	
	int Max = 0;
	int temp = 0;
	if(x[pos] <= y[pos]){ //進入迴圈
 		if(x[pos] != N && y[pos] == N) now++;
 		
		while(type[pos2] != _E){
			temp = jisuan(pos2);
			if(temp > Max) Max = temp;
		}
	}
	
	pos2++;
	now += temp;
	return now;
}

bool work()
{
	int Max = 0;

	for(int i=1; i<n ; i=pos2){
		if(type[i] == _F){
			pos2 = i;
			Max = max(Max, jisuan(i));
		}
	}

	if(Max == time) return true;
	else return false;
}

int main()
{
	freopen("text.in", "r", stdin);
	cin>>t;
	for(int i=0; i<t ;i++){
		Init();
		Input();
		if(complite())
			if(work()) cout<<"Yes"<<endl;
			else cout<<"No"<<endl;
		else cout<<"ERR"<<endl;
	}
	
	return 0;
}