【前言】:在學習了一些看上去只是能讓程序更好看更清晰的類的知識以後...爲了讓咱們真的以爲類是個好東西,喪心病狂的做業——魔獸出現了...然而...emmmm...ios
好吧,這篇博客主要就是記錄一下本身在打這些題的一些思路和想法...因爲這個題裏面其實沒有什麼算法和數據結構的運用,感受這篇博客成了一個教你們學習怎麼樣完成做業的blog hhh不過這樣也挺好。雖然這麼說啦,我會把我以爲個人程序裏本身以爲很漂亮的部分重點講一下,也能夠分享一些面對這樣一個比較複雜而冗長的代碼怎樣更好的提升調試效率的一些小方法。算法
由於直接讓你們作魔獸·終極版會直接讓你們放棄,因此老師很機智地設置了幾個按部就班的版本數組
【魔獸世界一:備戰】數據結構
筆者認爲呢,這個題的目的主要在於:1.營造場景 2.讓咱們體會一下面向對象和麪向過程之間的關係 3.emmm增長一下你們的信心,方便你們入坑(霧)編輯器
有N個城市,兩個指揮部,紅方和藍方。兩個指揮部都會製造武士,雙方會各自有一個製造武士的順序,而且每小時製造一個武士,同時製造武士須要代價,基地會有一個初始的值來消耗,而後若是當前順序能製造就製造,不能製造了就用順序的下一個,直到全部的武士都不能製造,再宣稱中止。ide
而後魔獸一出現了:它須要讓你按時間輸出這些製造的武士。函數
emm你看我上面的措辭:「輸出這些製造的武士」hhh並非「製造這些武士並輸出...」工具
爲何會想到不去真的製造武士呢,首先是...懶hhh能少一個類就少一個,其次由於在這個題中的武士是沒有任何行爲的。輸出行爲能夠放在司令部中輸出學習
因此這題中我只設置了一個司令部的類。而後咱們來思索一下這個類裏面須要什麼呢?通常能夠先想想它進行的行爲:ui
【行爲】1製造一個武士 2中止製造武士
那麼在製造武士的時候須要知道1.當前司令部的生命元 2.當前要製造的武士是誰 3.當前武士的編號 4.如今的時間 5.這個武士須要多少的生命元 6這個武士有多少了
這裏咱們會發現:4和5 其實在乎義上應該設置成全局變量的,由於好比時間應該是每個函數均可以調用到的,而每一個武士須要用的生命元也是一個客觀的給出的條件,也應該是容許全部人調用的。
那麼咱們來考慮1236,其中又會發現3也不用考慮啦,由於編號就是時間+1...而後1咱們能夠在基地裏設置一個整型的HP。而2的話會發現和順序和上一次用的是誰有關係,因此須要一個記錄順序的數組Order[],還有一個Index來記錄上一個是誰,對於6的話呢,咱們能夠用一個桶Count[],每次製造了一個武士who以後就能使用:Count[who]++;
嗯嗯,這樣咱們就解決了這個類的成員了!而後就只有一個問題啦,怎樣經過Order[]數組和Index來得知下一個是誰呢?想來有不少方法啦...hhh個人代碼就是儘可能短一點,而後好懂啦hhh[這也是寫代碼很好的習慣啦,hhh好比who,Index這種變量]
1 Index++; 2 int who=Order[Index%5],tmp=0; 3 while(HP<Cost[who] && tmp<5) 4 who=Order[++Index%5],tmp++;
而後就沒有難的地方啦!作完以後能夠沾沾自喜了(誒,這貌似是一個貶義詞Hhh...無論啦) (果真是誘惑入坑的好題...)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 5 using namespace std; 6 7 int Time_Index; 8 int Cost[6]; 9 int ord1[5]={3,4,5,2,1},ord2[5]={4,1,2,3,5}; 10 char Warrior_Name[6][7]={"\0","dragon","ninja","iceman","lion","wolf"}; 11 12 class Headquarter{ 13 public: 14 int Index; 15 int HP; 16 int Count[6]; 17 int Order[5]; 18 char Name[5]; 19 bool STOP; 20 Headquarter(int HP_,char *s,int *ord){ 21 Time_Index=-1; 22 Index=-1; 23 HP=HP_; 24 for(int i=1;i<=5;i++) Count[i]=0; 25 for(int i=0;i<5;i++) Order[i]=ord[i]; 26 memset(Name,0,sizeof(Name)); 27 strcpy(Name,s); 28 STOP=false; 29 } 30 void Build(){ 31 Time_Index++; 32 Index++; 33 if(STOP) return; 34 int who=Order[Index%5],tmp=0; 35 while(HP<Cost[who] && tmp<5) 36 who=Order[++Index%5],tmp++; 37 if(HP>=Cost[who]){ 38 HP-=Cost[who]; 39 Count[who]++; 40 printf("%03d %s %s %d born with strength %d,%d %s in %s headquarter\n",Time_Index,Name,Warrior_Name[who],Time_Index+1,Cost[who],Count[who],Warrior_Name[who],Name); 41 } 42 else{ 43 printf("%03d %s headquarter stops making warriors\n",Time_Index,Name); 44 STOP=true; 45 } 46 } 47 }; 48 49 int main(){ 50 #ifndef ONLINE_JUDGE 51 freopen("x.in","r",stdin); 52 freopen("x.out","w",stdout); 53 #endif 54 int Kase,W; 55 char s1[4]="red",s2[5]="blue"; 56 scanf("%d",&Kase); 57 for(int T=1;T<=Kase;T++){ 58 printf("Case:%d\n",T); 59 scanf("%d",&W); 60 for(int i=1;i<=5;i++) 61 scanf("%d",&Cost[i]); 62 Headquarter r(W,s1,ord1),b(W,s2,ord2); 63 while(!r.STOP || !b.STOP){ 64 r.Build(); 65 b.Build(); 66 } 67 } 68 return 0; 69 }
【魔獸世界二:裝備】
魔獸二是魔獸一的一個增強版,這個裏面呢,咱們仍是要製造和輸出武士的信息,可是這個題目中最重要的就是出現了裝備!
哇,簡直是一個新世界啦...由於這樣的話,咱們就有新的對象須要出現了!
在以前的魔獸世界一,武士這個類實際上是沒有什麼用的,他們只要提供名字和編號就能夠了。可是在魔獸世界二里若是咱們接着這樣作的話,就會有些麻煩咯,由於須要給武士們發武器了,那麼這樣就一下誕生了兩個對象:武器和武士...
[固然啦這個題裏其實武器也是沒有任何做用的,更容易的打法能夠刪掉武器這個類哦...不過筆者感受要開始爲魔獸三作點準備了就寫了這個類]
咱們注意到在魔獸一里武士們是沒有任何區別的,只有HP值和Name的不一樣。
可是在這一題中:咱們發現這些武士們開始偷偷分化了!而後就會有不少細節啦,有時候半句話就是一個小細節。
[這種題最容易錯的就是細節,筆者有一個好方法:就是你能夠先大概建好整個程序,而後一行一行地去看題目的描述,每看一句就去噼裏啪啦敲好QwQ,這樣就不會漏掉細節了哦...emmm固然!也能夠畫圖或者畫表來讓題目中的信息更加的清晰]
嗯嗯這個題中由於各類武士的不同,因此就能夠開始感覺虛函數的好處啦。
虛函數和多態在筆者看來是一個很實用的東西,它實際上是相似 If 的一個函數,一樣的一句話,if 這個函數的主體是x就執行 x 裏寫的函數,if 是 y 來作就執行 y 裏寫的函數。可是這個卻不是在編譯中寫好的 if 而是執行的時候纔去執行,這樣就讓代碼看上去邏輯感很好,並且也很簡潔啦!
而後筆者以爲邏輯這種東西,在寫一個比較長比較複雜的程序的時候是很是重要的,在保證必定的可讀性(這個須要變量名取得好哦...多用'_'或者首字母大寫來給變量名命名會很好)的狀況下,若是你的程序邏輯清楚的話,是很容易找到錯誤的。
因此我在用virtual的時候通常是先去思考一個通常武士的行動,而後思考是否是有特例的武士不會這麼作,那麼就設置成virtual,好比看着題目的輸出樣例,你就能夠很清楚的知道有:出生->獲得武器->輸出本身狀況 這樣三個廣泛的行動,可是每個部分都是有特例的,好比出生的時候有人會得到士氣和忠誠這樣的屬性。獲得武器的時候有人會獲得兩把,輸出本身時有的人沒有武器要輸出....[p.s.]不過出生的時候不同,寫在構造函數裏不同就好啦,這個就不能virtual了
而後這個題也就沒有困難啦...
【魔獸二:代碼】
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 6 using namespace std; 7 8 int Cost[5],Time_Index; 9 int ord1[5]={2,3,4,1,0},ord2[5]={3,0,1,2,4}; 10 char Weapon_Name[3][6]={"sword","bomb","arrow"}; 11 char Warrior_Name[5][7]={"dragon","ninja","iceman","lion","wolf"}; 12 char Headquarter_Name[2][5]={"red","blue"}; 13 14 class Weapon{ 15 char Name[6]; 16 public: 17 Weapon(int which){ 18 strcpy(Name,Weapon_Name[which]); 19 } 20 void print(){printf("It has a %s",Name);} 21 void Name_print(){printf("%s",Name);} 22 }; 23 24 class Warrior{ 25 protected: 26 int HP; 27 char Name[7]; 28 Weapon *w1,*w2; 29 public: 30 Warrior(int HP_,char *s){ 31 memset(Name,0,sizeof(Name)); 32 HP=HP_; 33 strcpy(Name,s); 34 w1=w2=NULL; 35 } 36 virtual void get_weapon(){ 37 w1=new Weapon((Time_Index+1)%3); 38 } 39 void Name_print(){ 40 printf("%s",Name); 41 } 42 virtual void Weapon_print(){ 43 w1->print(); 44 printf("\n"); 45 } 46 ~Warrior(){if(w1) delete w1;if(w2) delete w2;} 47 }; 48 //0 49 class Dragon:public Warrior{ 50 double morale; 51 public: 52 Dragon(int HP,double m_):Warrior(HP,Warrior_Name[0]){morale=m_;} 53 virtual void Weapon_print(){ 54 w1->print(); 55 printf(",and it's morale is %.2lf\n",morale); 56 } 57 }; 58 //1 59 class Ninja:public Warrior{ 60 public: 61 Ninja(int HP):Warrior(HP,Warrior_Name[1]){} 62 virtual void get_weapon(){ 63 w1=new Weapon((Time_Index+1)%3); 64 w2=new Weapon((Time_Index+2)%3); 65 } 66 virtual void Weapon_print(){ 67 w1->print(); 68 printf(" and a "); 69 w2->Name_print(); 70 putchar('\n'); 71 } 72 }; 73 //2 74 class Iceman:public Warrior{ 75 public: 76 Iceman(int HP):Warrior(HP,Warrior_Name[2]){} 77 }; 78 //3 79 class Lion:public Warrior{ 80 int loyalty; 81 public: 82 Lion(int HP,int l_):Warrior(HP,Warrior_Name[3]){loyalty=l_;} 83 virtual void get_weapon(){} 84 virtual void Weapon_print(){ 85 printf("It's loyalty is %d\n",loyalty); 86 } 87 }; 88 //4 89 class Wolf:public Warrior{ 90 public: 91 Wolf(int HP):Warrior(HP,Warrior_Name[4]){} 92 virtual void get_weapon(){}; 93 virtual void Weapon_print(){}; 94 }; 95 96 class Headquarter{ 97 private: 98 char Name[5]; 99 int HP; 100 int Order[5]; 101 int Count[5]; 102 int Warrior_Index; 103 Warrior *cur; 104 bool STOP; 105 public: 106 Headquarter(char *s,int HP_,int* O_){ 107 memset(Name,0,sizeof(Name)); 108 memset(Count,0,sizeof(Count)); 109 strcpy(Name,s); 110 HP=HP_; 111 for(int i=0;i<5;i++) 112 Order[i]=O_[i]; 113 Warrior_Index=-1; 114 cur=NULL; 115 STOP=0; 116 } 117 void Change_HP(int HP_){ 118 HP=HP_; 119 memset(Count,0,sizeof(Count)); 120 Warrior_Index=-1; 121 cur=NULL; 122 STOP=0; 123 }; 124 void Build_Warrior(){ 125 if(STOP) return; 126 Warrior_Index=(Warrior_Index+1)%5; 127 int who=Order[Warrior_Index]; 128 int temp=0; 129 while(Cost[who]>HP && temp<5){ 130 Warrior_Index=(Warrior_Index+1)%5; 131 who=Order[Warrior_Index]; 132 temp++; 133 } 134 if(HP>=Cost[who]){ 135 Count[who]++; 136 HP-=Cost[who]; 137 switch(who){ 138 case 0: cur=new Dragon(Cost[0],(double)HP/Cost[0]);break; 139 case 1: cur=new Ninja(Cost[1]);break; 140 case 2: cur=new Iceman(Cost[2]);break; 141 case 3: cur=new Lion(Cost[3],HP);break; 142 case 4: cur=new Wolf(Cost[4]);break; 143 }; 144 cur->get_weapon(); 145 printf("%03d %s ",Time_Index,Name); 146 cur->Name_print(); 147 printf(" %d born with strength %d,%d ",Time_Index+1,Cost[who],Count[who]); 148 cur->Name_print(); 149 printf(" in %s headquarter\n",Name); 150 cur->Weapon_print(); 151 delete cur; 152 cur=NULL; 153 } 154 else{ 155 printf("%03d %s headquarter stops making warriors\n",Time_Index,Name); 156 STOP=true; 157 } 158 } 159 bool Stop(){return STOP;} 160 }; 161 162 Headquarter r(Headquarter_Name[0],0,ord1),b(Headquarter_Name[1],0,ord2); 163 164 int main(){ 165 #ifndef ONLINE_JUDGE 166 freopen("x.in","r",stdin); 167 freopen("x.out","w",stdout); 168 #endif 169 int Kase,W; 170 scanf("%d",&Kase); 171 for(int T=1;T<=Kase;T++){ 172 printf("Case:%d\n",T); 173 scanf("%d",&W); 174 Time_Index=0; 175 r.Change_HP(W); 176 b.Change_HP(W); 177 for(int i=0;i<5;i++) 178 scanf("%d",&Cost[i]); 179 while(!r.Stop() || !b.Stop()){ 180 r.Build_Warrior(); 181 b.Build_Warrior(); 182 Time_Index++; 183 } 184 } 185 return 0; 186 }
【魔獸世界三:開戰】
這個版本里面的魔獸世界呢...
很重要的兩個任務就是讓你的武士們能夠移動起來,而後兩個武士之間會發生互動(打架、搶武器什麼的)
下面就詳細講一下啦...
先講一講關於武士的移動!
【武士的移動】
這裏的移動涉及到的主要是武士和城市這兩個單位。
而這裏不一樣於以前兩道魔獸的地方是:我不只要輸出每個移動,而且在我程序構建出來的這個世界中,武士是真的在移動的。
那麼咱們仍是先考慮這個部分的輸出:
由於城市是分普通城市和指揮部的,因此對應的輸出也是有兩種
先看看普通城市的輸出:(這些都是從提供數據中的out文件裏提出來的哦...)
000:10 red iceman 1 marched to city 1 with 117 elements and force 50
再看到達指揮部的輸出:
012:10 red wolf 3 reached blue headquarter with 5 elements and force 150 012:10 blue headquarter was taken
注意到兩種的區別!首先藍色指揮部的名字"blue headquarter"取代了"city %d",其次,在魔獸世界三中,只要有武士到達對方指揮部,對方的指揮部就會被佔領,遊戲也就結束,此時就須要咱們輸出一行"blue headquarter was taken"
【Tips1】這裏有一個很容易錯的地方,那就是當前Case結束輸出的時機!若是最後遊戲是平局,那麼應該是在時間結束的時候也結束輸出的。可是若是中途發生了指揮部被佔領的狀況就應該結束啦,也就是這個時刻以後的都不用輸出啦...!而在這個地方還有一個要注意:雖然在司令部被佔領以後遊戲結束,可是這個時刻的輸出仍是要輸出完的,好比說紅司令部被佔領了,可是這個時刻我纔剛剛枚舉了移動到第0個城市的武士,但是還有不少武士也在這個時刻移動,應該把他們都輸出完了才能結束這個Case的輸出!
【Tips2】這裏提到一個打程序的小技巧:爲了讓咱們的代碼不會出錯,個人建議是在要輸出的地方,先寫一個print("");而後把題目中提供的要輸出的信息填進去。而後,再把題目中提到的特殊的信息給挖空拿掉,再在printf的後面想辦法把這些用一些方法給表示出來。這樣的話錯誤率會降到很低很低哦...
好比說對於一個普通城市的輸出,個人代碼呢就長這樣了...
printf("%03d:10 %s %s %d marched to city %d with %d elements and force %d\n",Time_Index,Headquarter_Name[Direction<0],Name,Born_Number,City_Index,HP,MP);
hhh不過我一直覺得你們都是這麼打的...tips強行湊字數hhh
而後咱們再來考慮一下有多個武士,最後輸出這些移動信息的順序:
大的方向呢是從0到n+1的城市方向,而且在同一座城市會先輸出紅色武士的信息,再輸出藍色武士的信息。【Attention】注意啦!這裏的城市是指雙方武士移動後所在的城市!而咱們一般會把移動武士和輸出武士一塊寫,那麼這個時候請注意啦...對於第 i 城市的輸出就要移動的是 i-1 城市的紅方武士和 i+1 城市的藍方武士,具體以下(筆者de出來的第一個bug就是這裏寫錯了QwQ)...
下面是筆者的代碼:嗯裏面的City是一個Warrior類的指針的二維數組,第一維的下標是這個城市的編號,而第二維的[0]或[1]分別存的是red和blue方的武士指針。[emmm大家可能以爲筆者你....你竟然不把City寫成類!好吧...我就是懶hhh由於這題中的City好像沒有什麼用]
for(int i=0;i<=City_Amount+1;i++){
if(i>0)
if(City[i-1][0]!=NULL) City[i-1][0]->Process(); if(i<=City_Amount) if(City[i+1][1]!=NULL) City[i+1][1]->Process();
}
看完上面這個代碼,不少人就放鬆了警戒(包括當初的筆者我本身...)感受很滿意呀...代碼思路這麼清楚了,能夠安心寫process去了。這時一個帥氣的男生停在了你的面前:STOP!(咳咳,就是筆者我...hhhh)這樣打會有一個問題,那就是當我移動個人紅色方的武士的時候,他確定會到下一個城市 (由於這裏尚未開始打架...) 那麼,當我枚舉下一個城市裏的紅色戰士的時候,發現:誒,你不是從上個城市過來的麼?我原來那個武士呢?算了算了就移動你把...而後不知不覺一輪枚舉直接把我紅方的武士送進了對面的指揮部...
唔,那怎麼解決呢?筆者的辦法是創建一個新城市,讓移動後的武士先住在新城市裏,等舊城市的全部武士都走完了,再讓新城市裏的人回來...具體過程呢能夠見最後的代碼啦...。
好了,如今終於安心了,能夠開始寫咱們的Process函數了。
首先咱們注意到題目中對移動這個操做有三種人:Iceman(邊走邊掉血),Lion(越走越膽小),Others。
那麼這個Process確定就是一個虛函數了,那咱們分別看一下這三種人。先是Others。
【Others】 我走路走的好好的!
不過好好的走須要什麼呢?
須要的信息1.我當前的位置 2.我往哪邊走,那麼這兩個信息能夠變成兩個值在初始化的時候搞定!
1.int City_Index 表示本身的位置 red初始值是0 ,blue 是 N+1
2.int Direction 表示本身的方向 red用1 blue用-1
這樣設計的話 City_Index+=Direction; 就讓這個武士走完了誒!
不行不行,你還得照顧一下那些在City裏找武士的人呀...因此咱們還得修改外層指向這個武士的指針:
City[City_Index][Direction<0]=NULL; City_Index+=Direction; New_City[City_Index][Direction<0]=this;
這裏不少小夥伴就不知道Direction<0是什麼了...hhh突然感受本身是科普小天使....
好比< > ==這種符號都是雙目的運算符,它們也是有返回值的,是一個bool類型!若是正確呢那就是1,不正確呢那就是0
因此若是我把red定爲0,blue定爲1的話Direction<0就能夠知道這個武士是哪一家的了!
而後上面代碼的New_City就是我所設想的新城市啦...
好了,如今咱們移動了武士本身,還改了城市裏的指針,是否是結束啦!嗯嗯,別忘了加上以前的輸出哦
1 virtual void Process(){ 2 City[City_Index][Direction<0]=NULL; 3 City_Index+=Direction; 4 New_City[City_Index][Direction<0]=this; 5 if(City_Index==0){ 6 Red_Lost=true; 7 printf("%03d:10 blue %s %d reached red headquarter with %d elements and force %d\n",Time_Index,Name,Born_Number,HP,MP); 8 printf("%03d:10 red headquarter was taken\n",Time_Index); 9 } 10 else if(City_Index==City_Amount+1){ 11 Blue_Lost=true; 12 printf("%03d:10 red %s %d reached blue headquarter with %d elements and force %d\n",Time_Index,Name,Born_Number,HP,MP); 13 printf("%03d:10 blue headquarter was taken\n",Time_Index); 14 } 15 else 16 printf("%03d:10 %s %s %d marched to city %d with %d elements and force %d\n",Time_Index,Headquarter_Name[Direction<0],Name,Born_Number,City_Index,HP,MP); 17 }
不要看代碼這麼長,其實主要是輸出有點長啦....QwQ,最重要的只有三行而已啦...
好了,下面討論一下Iceman
【Iceman】我前進就要掉血!但我仍是要前進!
就記得移動的時候HP-=HP/10哦...注意啦這個掉血是當前血量的10%,因此不用擔憂Iceman行軍路上失血過多而死hhh
而後是咱們的Lion
【Lion】我不想走啦!再逼我我就逃跑!
嗯嗯記得就是把Loyalty-=K就好啦,而後要補一個判斷,看看Loyalty是否是<0啦...
嗯嗯這個裏面的話呢個人處理就是逃兵一概殺掉emmm直接讓這個逃跑的被上層delete掉就好啦
【武士的戰鬥】
武士戰鬥最重要的兩個信息:武士、武器
而這兩個就分別是程序中最重要的兩個類。
這一題中的武器數目從最多2很快提高到了10,同時武器的屬於關係也再也不固定,而是能夠在不一樣的武士之間傳遞。而對於這樣的關係,我認爲指針是最好的選擇了。由於武器的對象在傳遞的時候實際上是不會消失的,因此當一個武器從A到B手上時,只用把B中一個空的指針變成A的指針,而後A中指向這個武器的指針定爲空就完成了一次武器的交接。(不過筆者還打聽到一種比較巧妙的方法哦:這個題裏能夠不要武器類的,由於武器這個對象其實沒有具體的什麼值,它的攻擊是主人根據主人來的,它惟一具備本身的屬性就是耐久度了,而這個咱們用一個數組來當成武器庫存耐久度也不是很麻煩,並且這樣的話其實傳遞武器排序武器都是很簡單的了...不過筆者仍是講本身的類的作法吧)
1 class Warrior{ 2 int Weapon_Count;//Weapon_Count表示武器的數量 3 Weapon* w[10]; 4 };
上面解決了武士和武器之間的關係,咱們在這個基礎上再來看看打鬥中的各類操做應該如何實現
仍是根據故事的邏輯來看,把戰鬥按照時間順序分爲戰前、戰中、和戰後三個階段:(p.s.固然戰鬥發生的條件是這個城市裏有兩個武士哈)
【戰前】
戰前有一件對戰鬥很重要的事情:那就是wolf會搶奪武器!
邏輯上:這是一件很獨特的事情,能夠用virtual Before_Fight()來處理,也能夠if (Is_Wolf()) Rob_Weapon(Enermy)來處理
操做上:在搶奪以前:要知道搶奪發生的條件:
1,對方不是wolf,這樣須要一個判斷函數:virtual bool Is_Wolf();而後只有wolf類返回true其餘都返回false就能夠了
2.本身的武器數量<10 3.對方的武器數量>0,知道本身能夠搶奪以後,還不能立刻搶,這中間還有一個很重要的操做:你要搶編號最小的那一類武器,並且若是搶不完要優先搶使用次數少的。這樣的話咱們就要給敵人的武器按照剛纔的方法排序,筆者的習慣是用系統函數sort()搭配本身寫的cmp來排序,能夠把本身的cmp函數發上來供你們參考:
bool cmp(const Weapon *A,const Weapon *B){//這裏咱們是想給Warrior中的Weapon *w[]數組排序,因此應該比較兩個Weapon*的大小 if(A==NULL) return false;//咱們要讓NULL的元素都日後放,而sort中return true;就會把A放在前面,因此這裏應該返回false if(B==NULL) return true; if(A->Index!=B->Index) return A->Index<B->Index;//Index是武器的種類的編號(Sword-0,Bomb-1,Arrow-2),編號小的在前面,能夠看到這裏仍是很好的運用了<的返回值來使得代碼精簡(若是A的編號<B那麼就會return true讓A在前面) return A->Can_use>B->Can_use;//Can_use是武器的耐久度,初始值是2,bomb每使用一次會-2,Arrow使用一次會-1,耐久度大的放前面,因此是>號 }
[ 在上面的cmp函數中,我順帶介紹了本身關於Can_use的這個設計...嗯嗯感受我這樣挺漂亮的算,一個好設計hhh,而後同時也科普了一下cmp函數的寫法:return true會讓A放在B的前面,而後運用<和>號可讓本身的代碼很精簡。]
而後就是搶奪的過程了,這個就是武器從屬權的一個傳遞,在上面的指針部分也有介紹。若是還有不懂能夠看下面的搶奪代碼
1 virtual void Before_fight(Warrior *Enermy){ 2 if(Enermy->Is_Wolf()) return; 3 if(Weapon_Count<10 && Enermy->Weapon_Count>0){ 4 sort(w,w+10,cmp); 5 sort(Enermy->w,Enermy->w+10,cmp); 6 int Min_Index=Enermy->w[0]->Index,Amount=0; 7 for(int i=0;Enermy->w[i]!=NULL && Enermy->w[i]->Index==Min_Index && Weapon_Count<10 && i<Enermy->Weapon_Count+Amount;i++){ 8 w[Weapon_Count++]=Enermy->w[i],Amount++; 9 Enermy->Weapon_Count--; 10 Enermy->w[i]=NULL; 11 } 12 printf("%03d:35 %s wolf %d took %d %s from %s %s %d in city %d\n",Time_Index,Headquarter_Name[Direction<0],Born_Number,Amount,Weapon_Name[Min_Index],Headquarter_Name[Direction>0],Enermy->Name,Enermy->Born_Number,City_Index); 13 } 14 }
【戰中】
戰鬥的過程是兩我的互相使用武器的過程。
首先,仍是須要先給兩我的的武器排好序來肯定使用的順序,而後就相似魔獸世界一二中生產武士的方法來循環這個武器的順序:
1 int temp=0; 2 while(w[Weapon_Index=(Weapon_Index+1)%10]==NULL && temp<10) temp++;
上面的代碼就是幫助我找到下一個能夠用的武器(夠精簡吧hhh),而後就是使用武器的過程了,這裏武器的使用,只和使用者、被使用者、武器類型有關,而與具體的武器對象惟一的關聯就是耐久度這個屬性(因此纔會誕生出不設置武器類的作法),那麼咱們在使用武器的時候就須要把這些因素都考慮進去,同時不一樣類型的武器是不同的,因此咱們須要用到虛函數來處理。在這個過程當中是可能致使武士死亡的,可是咱們如今不能急着把武士給delete掉,由於整個武士的最頂級指針是最上層的City數組,並且等一會還有敵人來收繳武器,因此咱們能夠用一個bool Die來表示一個武士是否死亡,而後延遲delete的時間,在打鬥的時候不delete,等戰鬥發生完了之後回到上層再去刪除。
而後用完了武器就會面臨武器可能失效的問題,這個時候要刪除這個武器,由於武器的最上層控制者就是武士,因此這個delete的操做也應該是放在武士的函數裏執行的。同時記得w[i]=NULL和Weapon_Count--的操做。
戰中還有一個很重要的過程就是宣佈戰中結束,進入戰後。有兩種狀況是很容易判斷的:1. 有一方死亡時 2. 雙方都沒有兵器時
還有一種狀況是比較複雜的,那就是雙方的攻擊力<5(這時使用Sword將會形成0傷害),而後互相打到地老天荒,這個時候怎麼辦呢?
室友想到一種很偷懶的方法,就是執行完1000輪還不結束,我就宣告平局...emmm顯然能夠造數據卡掉你嘛,,不過最後數據確實沒有卡(由於如今尚未看到任何一個TLE的同窗...)hhh懷疑那些運行比我(2ms)慢的(24ms)可能就是用了這種方法?
個人辦法會比較符合邏輯一點,就是我會有一個倒計時Time_Tick,它的初始值是兩人的武器數量中大的一個的兩倍,那麼當倒計時執行完以後,即便是武器數量大的那我的也只可能有sword這件武器了,就進入了白熱化階段,而後你再判斷一下雙方的攻擊力,或者判斷一下場面是否是還有變化,這樣就能夠結束了。
【戰後】
戰後首先會有一個叫Dragon的兄弟,若是沒死他就會yell一下,記得輸出。
而後戰後主要就是死亡以後的收繳兵器。由於兵器的最高控制者就是武士,因此寫在武士的函數裏(這裏提到了不少次最高控制者,由於筆者程序中的指針每每表示的是控制關係,因此每當我想刪除一個東西的時候,就須要去考慮誰的指針仍是指着它的,那麼我會讓這個刪除操做發生在哪一個指揮者的函數中)
而收繳兵器的過程和wolf搶兵器的過程是幾乎同樣的,只是不僅能夠搶一類武器了,去掉那個終止條件就能夠了。
那麼戰鬥也就分析完了...
最後來看看怎麼讓你的程序能夠AC吧:
【關於如何從CE->WA】
啊,這個過程能夠說是很痛苦了...不過相信大家能夠的hhh
【最後:關於怎樣調試代碼(WA/RE/TLE->AC)】
首先呢,筆者本身對這道題大概debug了兩天左右...其實感受本身debug已經頗有方法了,只是前面寫代碼的時候好像太粗糙了
[其中的情緒變化大約是:[冷靜] 這裏有錯誒?-> [急躁] 怎麼還有錯?!-> [抓狂] 還有哪有錯呀?-> [目光呆滯] 沒錯了....吧...]
因此,你們本身敲第一遍代碼的時候必定要謹慎!當心!要跟着本身的光標一塊兒思考這個位置應該是什麼,這樣打是否是必定對?
這個過程必定要謹慎哦...爲了你們調試代碼的愉悅度!
調試的過程:
【關於調試工具】
emmm 筆者用的是gdb這種原始又實用的東西 hhh而後你們就各自用本身的IDE好了!
首先你們須要知道基本的【文件操做】來讓本身的程序能夠讀一個比較大的輸入文件,也能把輸出的東西存到一個文件裏面去
1 #ifndef ONLINE_JUDGE 2 freopen("data.in","r",stdin); 3 freopen("my_code.out","w",stdout); 4 #endif
只要把這個東西加在int main的後面就能夠啦(前面須要包含<cstdio>庫),其中二、3行是正式的代碼啦,而後一、4行可讓你的程序在本身本機運行的時候用二、3裏面的語句,可是交到openjudge上的時候又不會調用裏面的語句,是否是很厲害呢hhhh
而後你們既然都輸出到文件裏面了,那就固然也要知道一種能夠比較文件是否相同的辦法!由於要肉眼比較輸出有一點麻煩...【不過我就是這麼作的】,若是你要這麼作的話,請選一個好看的文本編輯器hhh:肉眼比較一次能夠比較一版,就是將兩個out調整到相同的位置(這個調整到相同位置也是頗有技巧的,你要靈活的使用鼠標和光標來調整位置hhh),而後反覆切換,在來回切換的時候不一樣點會很明顯,這樣你就能夠找到了!
而後言歸正傳仍是講一講【怎麼比較文件】
首先是Linux最熟悉啦:在當前位置打開終端,而後輸入:diff -c my_code.out std.out 就能夠比較了(其中-c是能夠幫你輸出錯誤行號的,感受一看就明白啦...而後my_code.out是本身的輸出結果,std是正確的結果 [這個正是我用的hhh])
而後是Windows下:先敲一下cmd調出命令行,而後cd 後面加你文件存的位置,這個位置怎麼找呢?就是你能夠打開你文件所在的文件夾,而後單擊一下,而後cd + 這個藍色的東西...+回車 (我真是科普小天使呀hhh)
而後你就進入到了當前文件夾,而後輸入命令:fc my_code.out std.out+回車就能夠啦...而後這個不一樣可能不少,致使控制檯裝不下..[hhh我就常常發生這種狀況]
咱們能夠fc my_code.out std.out > Difference.txt 這樣就能夠把比較信息輸出到這個txt裏面去了,上面Linux也是同樣的哦
而後是根據讀者範圍友情添加的Mac大神應該怎樣作呢?
Hhh我也不知道啦...不過下面這個博客提供了兩種文件比較的工具,能夠看一下啦 :https://blog.csdn.net/wowfly98/article/details/52774275
【關於數據】
想要調試好,你須要得到一些比較優秀的數據,course上提供的數據就挺棒的(由於過了那個就能A啦!hhh)
不過呢course上的數據是多組的,爲了方便調試,咱們能夠一組一組的拆開來試【by the way 不知道blog怎麼上傳文件QwQ那就你們本身來吧...hhh個人經驗是第一二組數據是很好的數據,後面的九十也都是比較強的,而後中間有一些是很短的數據,價值不高】
而後對於一組數據咱們也是能夠拆的!至於爲何要拆呢?請聽下面的筆者哭訴節目:
筆者當時常常會遇到RE。RE是一種比WA更難受的東西,它有時是很良心的:會告訴你卡在哪一行了...有時不會告訴你,它就是卡住了!(太傲嬌了吧)
RE在這種涉及到一堆指針的題目裏面是十分常見的,覺得你只要delete了一個空的地址就會報錯,而後訪問了一個空指針的內容也會RE,因此delete以後必定記得把指針賦值爲NULL!記得要訪問以前判斷是否是NULL!
而後這裏涉及到的指針主要是City和New_City的Warrior指針,還有就是Warrior裏面放Weapon的w[]指針數組。City要記得每一個Case以後要清空一下,順帶把指向的戰士消除掉,而後w[]數組是很容易出錯的一個地方!由於中途我會有武器損耗、武器被搶等等均可能將w[i]中間某一個位置給挖空!這樣的話w[]中有指向的範圍就不是連續的了!Weapon_Count也只能給你計數了,因此每次我想要一塊連續的武器存儲必定要記得排序,並且排序的範圍必定是[0,10)哦(筆者有n次RE都是這個緣由,由於須要用Weapon的地方太多了,改的時候記得一塊兒改完)
不過也不必定都是這種狀況的RE,對於RE咱們有傳統方案就是設置斷點,在這種時間不少的題中的斷點通常長這樣:
1 if(Time_Index==7) 2 printf("Stop\n");
經過不停地修改Index後面的數值就能夠知道在哪一個時間段裏RE啦,而後再單獨進入裏面調試。
固然啦,這個題有一個得天獨厚的優點:就是輸入數據中是有時間的,因此你只要修改後面的時間也能夠打到經過設置斷點找到RE的時間點的過程,同時你也能夠經過修改時間找到一個裏RE比較近的能夠運行的時間去觀察輸出數據中關於時間的輸出來方便本身設置斷點來檢查RE。
而後關於RE的狀況在上面已經講得比較豐富了...
下面來說講WA的調試方法。
魔獸三這樣的題實際上是很容易找到WA的問題的,由於咱們是面向對象的程序,並且咱們的輸出必定是有一個對象的,那麼你在哪個位置WA了,必定是針對某一個對象的儲存值出現了問題,而咱們是能夠在這個WA的地方以前找到關於這個對象全部的報告的,方法就是control+F而後輸入你想找的對象名字 [這個時候又須要你有一個優秀的文本編輯器啦hhh]好比個人界面是這樣子的:
好比說打架的結果不一樣了,我就去看一下前面彙報的兩我的的血量生命值仍是有武器數量什麼的...而後就能夠手上模擬看看程序在哪兒出問題啦..
總而言之WA雖然可能的誘因無窮無盡,可是相對於RE也是更容易找到並且找的過程更加的有趣的hhh,固然關於WA的誘因,最重要的一部分就是題裏面的細節
那我就列舉一些我印象深入的害人的細節吧:
1.題面描述裏:這裏在40分鐘和50分鐘之間混入了一個10分鐘輸出...但事實上最後輸出是須要在10分的時刻輸出的(詳細見上面描述行軍的部分)
2.Dragon沒有戰死就會歡呼
3.Ninja使用炸彈本身不會掉血
4.使用炸彈可能把本身炸死而敵人不死,這時候敵人也會收繳你的武器
5.當指揮部被佔領後遊戲結束,可是仍是要輸出這個時刻全部武士的移動報告
6.Wolf不會搶Wolf的武器;Wolf只會搶一種武器;若是武器是用cnt[3]存的話,Wolf要當心一塊兒搶光使得本身的武器多於10把的狀況。
若是是和筆者同樣用的數組存儲武器的話,要注意下面這個程序段中的for循環中的終止條件應該寫成:i<Enermy->Weapon_Count+Amount
而不能寫成i<Enermy->Weapon_Count,由於每當我搶一件武器,個人Weapon_Count都會-1,可是我搶的武器確實從頭搶起的,因此若是我寫成了<Weapon_Count的話就會使得最後幾件武器搶不到了,因此這個終止條件應該是搶武器以前的武器數量,也就是Weapon_Count+Amount了。
1 sort(w,w+10,cmp); 2 sort(Enermy->w,Enermy->w+10,cmp); 3 int Min_Index=Enermy->w[0]->Index,Amount=0; 4 for(int i=0;Enermy->w[i]!=NULL && Enermy->w[i]->Index==Min_Index && Weapon_Count<10 && i<Enermy->Weapon_Count+Amount;i++){ 5 w[Weapon_Count++]=Enermy->w[i],Amount++; 6 Enermy->Weapon_Count--; 7 Enermy->w[i]=NULL; 8 }
7.輸入的時間的單位是分鐘;若是雙方司令部都中止生產了而時間尚未到,也得接着輸出(不一樣於魔獸一二)
8.這個題中的指揮部生產士兵,若是下一個不能生產了就不會再生產,而不須要去找下一個能產的(不一樣於一二)
9.這個題中也會分發武器,可是不須要輸出分發武器的信息;這一題中Lion出生也會發兵器(不一樣於魔獸二)。
10.使用swith case的時候必定記得break!
[上面都是筆者的痛苦教訓,但願大家能夠省些力氣哦...]
【魔獸世界三:代碼】