牛客程式設計題:彩色寶石項鍊
彩色寶石項鍊
題目描述:
有一條彩色寶石項鍊,是由很多種不同的寶石組成的,包括紅寶石,藍寶石,鑽石,翡翠,珍珠等。有一天國王把項鍊賞賜給了一個學者,並跟他說,你可以帶走這條項鍊,但是王后很喜歡紅寶石,藍寶石,紫水晶,翡翠和鑽石這五種,我要你從項鍊中擷取連續的一小段還給我,這一段中必須包含所有的這五種寶石,剩下的部分你可以帶走。如果無法找到則一個也無法帶走。請幫助學者找出如何切分項鍊才能夠拿到最多的寶石。
輸入描述:
我們用每種字元代表一種寶石,A表示紅寶石,B表示藍寶石,C代表紫水晶,D代表翡翠,E代表鑽石,F代表玉石,G代表玻璃等等,我們用一個全部為大寫字母的字元序列表示項鍊的寶石序列,注意項鍊是首尾相接的
輸出描述:
輸出學者能夠拿到的最多的寶石數量。每行一個
示例
輸入: ABCYDYE 輸出: 1
輸入: ATTMBQECPD 輸出: 3
思路分析:窮舉暴力法,利用兩個指標,將項鍊進行切割,兩個指標中需要包含ABCDE這五種寶石,項鍊總長度減去這段序列即未能獲得的最大寶石數。
例項1的演算法思路圖解
#include<iostream> #include<string> #include<cstring> using namespace std; int main() { string inputStr; while (cin >> inputStr) { int strLength = inputStr.size(); int maxGet = 0;//能取得的最大寶石數 //輸入的寶石順序的第一個開始尋找一段序列,並且逐漸移動指標 for (int beginPtr = 0; beginPtr < strLength; ++beginPtr) { int flags[5] = { 0 };//由於標記五種寶石是否找到 int endPtr = beginPtr;//尾指標, int cnt = 0;//選中的寶石序列中寶石個數 int sumFlag = 0;//序列中已經找到的特殊寶石總數 while (sumFlag < 5 && cnt < strLength) { //序列中已找到的特殊寶石總數小於5,且序列中的寶石數小於寶石總數 if (endPtr < strLength) { //**因為初始寶石鏈是環狀**,所以尾指標可能移到頭指標的前面 if (inputStr[endPtr] >= 'A' && inputStr[endPtr] <= 'E' && flags[inputStr[endPtr] - 'A'] == 0) { //如果尾指標指向的寶石(即將新增的寶石)是特殊寶石,且該寶石之前沒有遇到 flags[inputStr[endPtr] - 'A'] = 1;//標記已找到 ++sumFlag;//找到的特殊寶石總數自增 } ++cnt;//不管有沒有找到,寶石序列都增加了一個長度 ++endPtr;//尾指標後移 } else { //如果尾指標移動了頭指標的前面,將序號對strLength求餘即可 if (inputStr[endPtr % strLength] >= 'A' && inputStr[endPtr % strLength] <= 'E' && flags[inputStr[endPtr % strLength] - 'A'] == 0) { //如果尾指標指向的寶石(即將新增的寶石)是特殊寶石,且該寶石之前沒有遇到 flags[inputStr[endPtr % strLength] - 'A'] = 1;//標記已找到 ++sumFlag;//找到的特殊寶石總數自增 } ++cnt;//不管有沒有找到,寶石序列都增加了一個長度 ++endPtr;//尾指標後移 } } if (maxGet < strLength - cnt) { //如果上一次尋找能獲得的最大寶石數小於此次獲得的,則更新 maxGet = strLength - cnt; } } //輸出結果 cout << maxGet << endl; } return 0; }
上面的程式碼通過了牛客的測試。
恭喜你通過本題
執行時間:4ms
佔用記憶體:480k
對演算法進行優化:注意到,上面的程式碼每次頭指標向後移動後都是直接放入中間截出的序列中,並沒有對頭指標指向的寶石種類進行判斷。不難發現,如果頭指標指向的寶石不是特殊寶石,那麼將頭指標指向的寶石從中間截出的序列段中移去,操作後的截出序列任然滿足條件,從而達到了縮減序列長度的效果,繼而減少了尋找次數。優化思路,增加對頭指標指向的寶石型別進行判斷的功能。
#include<iostream> #include<string> #include<cstring> using namespace std; int main() { string inputStr; while (cin >> inputStr) { int strLength = inputStr.size(); int maxGet = 0;//能取得的最大寶石數 //輸入的寶石順序的第一個開始尋找一段序列,並且逐漸移動指標 for (int beginPtr = 0; beginPtr < strLength; ++beginPtr) { if (inputStr[beginPtr] >= 'A' && inputStr[beginPtr] <= 'E') { //如果頭指標指向的寶石型別是特殊型別才進行尋找操作 int flags[5] = { 0 };//由於標記五種寶石是否找到 int endPtr = beginPtr;//尾指標, int cnt = 0;//選中的寶石序列中寶石個數 int sumFlag = 0;//序列中已經找到的特殊寶石總數 while (sumFlag < 5 && cnt < strLength) { //序列中已找到的特殊寶石總數小於5,且序列中的寶石數小於寶石總數 if (endPtr < strLength) { //因為初始寶石鏈是鏈狀,所以尾指標可能移到頭指標的前面 if (inputStr[endPtr] >= 'A' && inputStr[endPtr] <= 'E' && flags[inputStr[endPtr] - 'A'] == 0) { //如果尾指標指向的寶石(即將新增的寶石)是特殊寶石,且該寶石之前沒有遇到 flags[inputStr[endPtr] - 'A'] = 1;//標記已找到 ++sumFlag;//找到的特殊寶石總數自增 } ++cnt;//不管有沒有找到,寶石序列都增加了一個長度 ++endPtr;//尾指標後移 } else { //如果尾指標移動了頭指標的前面,將序號對strLength求餘即可 if (inputStr[endPtr % strLength] >= 'A' && inputStr[endPtr % strLength] <= 'E' && flags[inputStr[endPtr % strLength] - 'A'] == 0) { //如果尾指標指向的寶石(即將新增的寶石)是特殊寶石,且該寶石之前沒有遇到 flags[inputStr[endPtr % strLength] - 'A'] = 1;//標記已找到 ++sumFlag;//找到的特殊寶石總數自增 } ++cnt;//不管有沒有找到,寶石序列都增加了一個長度 ++endPtr;//尾指標後移 } } if (maxGet < strLength - cnt) { //如果上一次尋找能獲得的最大寶石數小於此次獲得的,則更新 maxGet = strLength - cnt; } } } cout << maxGet << endl; } return 0; }
恭喜你通過本題
執行時間:3ms
佔用記憶體:457k
程式碼簡化思路:不難發現,中間的while迴圈中,對endPtr討論分成了兩種情況,但顯得有點多餘,可以進行合併。因為小於strLength的數模上strLength後對本身沒有影響。
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main() {
string inputStr;
while (cin >> inputStr) {
int strLength = inputStr.size();
int maxGet = 0;//能取得的最大寶石數
//輸入的寶石順序的第一個開始尋找一段序列,並且逐漸移動指標
for (int beginPtr = 0; beginPtr < strLength; ++beginPtr) {
if (inputStr[beginPtr] >= 'A' && inputStr[beginPtr] <= 'E') {
//如果頭指標指向的寶石型別是特殊型別才進行尋找操作
int flags[5] = { 0 };//由於標記五種寶石是否找到
int endPtr = beginPtr;//尾指標,
int cnt = 0;//選中的寶石序列中寶石個數
int sumFlag = 0;//序列中已經找到的特殊寶石總數
while (sumFlag < 5 && cnt < strLength) {
//序列中已找到的特殊寶石總數小於5,且序列中的寶石數小於寶石總數
if (inputStr[endPtr % strLength] >= 'A' && inputStr[endPtr % strLength] <= 'E' && flags[inputStr[endPtr % strLength] - 'A'] == 0) {
//如果尾指標指向的寶石(即將新增的寶石)是特殊寶石,且該寶石之前沒有遇到
flags[inputStr[endPtr % strLength] - 'A'] = 1;//標記已找到
++sumFlag;//找到的特殊寶石總數自增
}
++cnt;//不管有沒有找到,寶石序列都增加了一個長度
++endPtr;//尾指標後移
}
if (maxGet < strLength - cnt) {
//如果上一次尋找能獲得的最大寶石數小於此次獲得的,則更新
maxGet = strLength - cnt;
}
}
}
//輸出結果
cout << maxGet << endl;
}
return 0;
}
如果程式碼有錯誤,或者有更好的思路,歡迎指正或指導。
小男子不才,前排大佬讓一讓。