這個題,巧用標記思想,便能事半功倍。
題目鏈接:
L1-043 閱覽室
思路
此題若按最暴力的方式,定義一結構體,裏面的成員有,書號,E或S狀態,小時,秒四個成員,而這樣定義的結構體不僅操作繁瑣,且正確性難以得到快速地驗證,很容易出錯。其實結構體內只需定義兩個成員即可,一個是借書的起始時間,另一個是當前書是否已經被借,這其中的關鍵是是否已經被借這個變量,體現了一種思想——標記思想。何爲標記思想?即定義一個某個重要變量是否存在的標識,這個標識一旦定義了,往往後續操作會比原來簡略很多。而這個被標識的重要變量往往是輸出變量的基礎,操作會大大減少,效率也會得到很大提升。
比如刪除B字符串在A中的所有字符,A,B中的字符均爲ASC碼中的字符,那麼就可以定義一個一維數組,以字符的ASC碼對應的值爲下標,下標對應的值只有0或1,0代表不存在字符,1代表存在字符,於是只要用一個循環初始化數組,用另一個循環來檢閱是否A是否存在B的字符即可,兩個循環O(N)複雜度解決。
此題也是用到了這樣的思想,借閱標識是輸出變量借閱次數的基礎,時間效率很高,只有O(N)複雜度,而最原始的暴力算法的複雜度將高達O(N^3),這就是標識思想的威力所在。如果本題不涉及時間,那麼標識變量就是所求變量的基礎,無需再定義結構體。直接定義book數組,數組下標是書號,數組元素是0或1,0代表未借,1代表已借。這表明,如果輸出變量只由一個因素決定,往往這個因素可以作爲標識,而標識表現爲某數組的元素值,非0即1,數組下標是輸入的某個數,這個數具有唯一性,能作爲索引表示某個概念。
AC代碼:
#include<iostream>
using namespace std;
typedef struct BOOK{
int startT;
int borrowed;
};//定義起始時間,借閱標識
BOOK book[1001];
int GetTime(int h,int m)
{
return h*60+m;//返回當前時間分鐘數
}
int main()
{
int N;
cin>>N;
for(int i=0;i<1001;i++)
book[i].borrowed=0;//初始化,置0,標識未借閱
int index,h,m,totalT,cnt;//定義書號,時,分,借閱時間,借閱次數
char c,state;//定義冒號,鍵值狀態
for(int i=0;i<N;i++)
{
totalT=cnt=0;//每天初始化爲0
while(1)
{
cin>>index>>state>>h>>c>>m;
if(!index)break;//若書號爲0,退出循環
if(state=='S')
{
book[index].startT=GetTime(h,m);//保存當前時間分鐘數
book[index].borrowed=1;//標識爲已借閱
}
else if(book[index].borrowed)//如果鍵值爲E且已借閱
{//此分支避免了只有E沒有S的書,如果某書只有E沒有S,則其標識一定是未借閱
totalT+=GetTime(h,m)-book[index].startT;//累加時間
cnt++;
book[index].borrowed=0;//因爲已經還書,標識爲未借閱
}
}
if(cnt)//如果借閱次數不爲0
cout<<cnt<<' '<<(int)(totalT*1.0/cnt+0.5)<<'\n';//輸出借閱次數和借閱時間
//注意需要先乘以1.0轉爲double,相除後再+0.5轉換爲int,這是一個測試坑點
else
cout<<"0 0\n";
}
return 0;
}