【提高組NOIP2017】時間複雜度 題解 分治系統處理
原題邊幅很長,這裡就不貼出來了,落谷有原題,不清楚的可以去看看
------------------------------------------------------------------------------------------------------------
這是一道大模擬,我的方法是建立輸入、編譯和判斷三大系統。
這三大系統用三個函式實現,分別是
void Input() //輸入系統,專門讀取資料並把字串內的有價值資料提取出來
bool complite() //編譯系統,對整組資料進行語法檢查,返回true代表語法正確
bool work() //判斷系統,符合時間複雜度返回
-------------------------------------------------------------------------------------------------------------------
主要流程:
所以主函式是這樣的:
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; }
----------------------------------------------------------------------------------------------------------------------------
那麼接下來的任務就很明確了,就是一個個實現三大系統
我們讀到的資料是字串,字串是不方便直接處理的,所以需要輸入系統的資料提取功能,把字串的有用的同類型資料轉化成一個個陣列存取,具體怎麼提取,還要看接下來編譯與判斷系統需要的是字串中的哪些資訊。在本題目中,我把一行字串看成由以下四類資料組成。
字串開頭非F即E,O(n^w)這行不需要進行編譯和判斷,只需要把
當string為F型(即開頭為F,E型以此類推)時才會有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;
}