题目大意:有很多个服务窗口,有一个窗口是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;
}