資料結構——銀行排隊問題之單佇列多視窗加VIP服務
題目大意:有很多個服務視窗,有一個視窗是VIP視窗,如果這個視窗有空閒,並且排隊等待的人裡有VIP,那麼VIP先去這個視窗辦理業務(SORRY,充錢就是了不起),如果果有多個視窗可以選擇,顧客會優先選擇視窗編號小的。
題目連結:https://pintia.cn/problem-sets/1042354461223579648/problems/1042355242509160453#p-6
在網上看到很多部落格評論這道題很噁心,在這裡也跟風一下,這道題確實很噁心。 改了很多次思路,最終AC。
這道題,推薦自己瘋狂嘗試後,在看看題解是如何給出的,這道題會幫助你踩出很多的思維漏洞,額。。。說到思維漏洞,還有題意,注意題目中所給的視窗的編號是從0開始的(心粗的一批)。
現在這裡說一下錯思路的最終版本:
在這道題之前有一個單佇列多視窗問題如果不知道,點這裡,保持那個問題的整體框架不變,對VIP視窗進行特別的判斷,當一個使用者的選擇是VIP視窗時,這時候就需要判斷,佇列中是否含有VIP會員,如果有的話,去找次的最快視窗,然後輸出。
程式碼實現:
#include <bits/stdc++.h> using namespace std; const int INF=0x3f3f3f3f; const int maxn=1000+10; struct node { int st; int work; int vip; bool operator < (const node&s ) const { return st<s.st||(st==s.st&&vip>s.vip); } }; struct nodes { int num; int endtime; nodes() {num=0; endtime=0;} }; node que[maxn]; nodes w[maxn]; bool used[maxn]; int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d %d %d",&que[i].st,&que[i].work,&que[i].vip); que[i].work = que[i].work>=60 ? 60: que[i].work; } sort(que,que+n); // for(int i=0;i<n;i++) // { // cout<<"輸出中間值 "<<que[i].st<<" "<<que[i].work<<" "<<que[i].vip<<endl; // } int k,vipw; scanf("%d %d",&k,&vipw); vipw++; int max_w=0,max_f=0,sumw=0; //cout<<"檢測"<<endl; for(int i=0; i<n; i++) { if(used[i]) continue; bool flag=false; int fw=INF,fn=INF; for(int j=1; j<=k; j++) { if(w[j].endtime<=que[i].st) { used[i]=true; w[j].num++; w[j].endtime=que[i].st+que[i].work; flag=true; //cout<<"輸出最後視窗 "<<fn<<" "<<i<<endl; break; } if(fw>(w[j].endtime-que[i].st)) { fw=w[j].endtime-que[i].st; fn=j; } } // cout<<"輸出過程值 "<<i<<" "<<fn<<" "<<flag<<endl; if(flag) continue; if(fn==1&&que[i].vip==0) { bool judge=false; int pos=0; for(int j=i+1;j<n;j++) { if(i+1<n&&que[j].vip==1&&que[j].st<=w[fn].endtime) { judge=true; pos=j; //cout<<"輸出搜尋位置的值 "<<j<<endl; break; } } if(judge) { int fw1=INF,fn1=INF; for(int j=1; j<=k; j++) { if(j==fn) continue; if(fw1>w[j].endtime-que[i].st) { fw1=w[j].endtime-que[i].st; fn1=j; } } used[pos]=true; used[i]=true; w[fn].num++; w[fn1].num++; max_w=max(max_w,w[fn].endtime-que[pos].st); max_w=max(max_w,w[fn1].endtime-que[i].st); sumw+=(w[fn].endtime-que[pos].st+w[fn1].endtime-que[i].st); w[fn].endtime=w[fn].endtime+que[pos].work; w[fn1].endtime=w[fn1].endtime+que[i].work; //cout<<"gg2 "<<max_w<<endl; //cout<<"輸出最後視窗 "<<fn<<" "<<i<<endl; } else { used[i]=true; w[fn].num++; max_w=max(max_w,w[fn].endtime-que[i].st); sumw+=(w[fn].endtime-que[i].st); w[fn].endtime=w[fn].endtime+que[i].work; //cout<<"gg2 "<<max_w<<endl; //cout<<"輸出最後視窗 "<<fn<<" "<<i<<endl; } } else { used[i]=true; max_w=max(max_w,w[fn].endtime-que[i].st); w[fn].num++; sumw+=(w[fn].endtime-que[i].st); w[fn].endtime=w[fn].endtime+que[i].work; //cout<<"gg2 "<<max_w<<endl; //cout<<"輸出最後視窗 "<<fn<<" "<<i<<endl; } } for(int i=1;i<=k;i++) max_f=max(max_f,w[i].endtime); //cout<<"輸出總的等待時間 "<<sumw<<endl; printf("%.1f %d %d\n",sumw*1.0/n,max_w,max_f); int isfirst=1; for(int i=1;i<k;i++) { if(!isfirst) printf(" "); if(i==vipw) { printf("%d %d",w[1].num,w[i+1].num); } else printf("%d",w[i+1].num); isfirst=false; } return 0; }
但是提交玩成後只能獲得25分,然後就開始尋找自己的錯誤,最後花了兩個小時,才濾出了自己的漏洞,這應該也是這道題最噁心的地方。
當一個普通使用者選擇VIP視窗時,我們執行的操作是讓她去尋找另一個次快的視窗,但是這裡有個問題,就是那個插隊的VIP辦理業務非常快,你仍然要在那個VIP視窗排隊,然後需要再一次的判斷佇列中是否有VIP,如果下一個VIP辦理業務依舊很快,你仍然在VIP視窗排隊,你需要再次考慮。。。。。。如此的迴圈下去,(PS:這個是一個簡單的For迴圈能夠解決,可以自己動手試一試)
然後就是正確的思路,暴力列舉時間流逝一i秒一秒的判斷,因為最多有1000個人,而且沒人辦理業務的時間最多是60S,所以總時間最多就是60000,時間複雜度很小。
下面給出AC程式碼:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2000;
const int INF=0x3f3f3f3f;
struct node
{
int st,et,vip;
};
node que[maxn];
int wd[maxn];
bool used[maxn],te[11][maxn*60];
int main()
{
int n; scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d %d %d",&que[i].st,&que[i].et,&que[i].vip);
que[i].et= que[i].et>=60 ? 60 : que[i].et;
}
int k,vipw; scanf("%d %d",&k,&vipw);
int cnt=n,sumw=0,max_w=0,max_f=0;
for(int t=0;cnt;t++)
{
if(te[vipw][t]==false)
{
for(int i=0;i<n;i++)
{
if(used[i]||!que[i].vip) continue;
if(que[i].st>t) break;
wd[vipw]++;
max_w=max(max_w,t-que[i].st);
max_f=max(max_f,t+que[i].et);
sumw+=(t-que[i].st);
cnt--;
used[i]=true;
for(int j=0;j<que[i].et;j++) te[vipw][t+j]=true;
break;
}
}
for(int i=0;i<k;i++)
{
if(te[i][t]==false)
{
for(int j=0;j<n;j++)
{
if(used[j]) continue;
if(que[j].st>t) break;
wd[i]++;
max_w=max(max_w,t-que[j].st);
sumw+=(t-que[j].st);
cnt--;
used[j]=true;
max_f=max(max_f,t+que[j].et);
for(int h=0;h<que[j].et;h++) te[i][t+h]=true;
break;
}
}
}
}
printf("%.1f %d %d\n",sumw*1.0/n,max_w,max_f);
for(int i=0;i<k;i++)
{
if(i) printf(" ");
printf("%d",wd[i]);
}
return 0;
}