题目描述
试判断一个消息队列是否可能死锁。
消息队列的缓冲区长度为L单位,读操作为每次从缓冲区读取R单位,写操作为每次写入缓冲区W单位。
消息队列会持续进行读写操作。具体为写操作会在缓冲区还剩余大于等于W单位空间时保持进行,当缓冲区内空间小于W时,写操作停止,等待读操作进行;类似的,读操作会在缓冲区可读内容大于等于R时保持进行,当可读内容小于R时,读操作停止,等待写操作进行。读写都是原子操作。
若读写操作均无法进行,定义此时状态为死锁。给定L,R,W,问消息队列是否可能进入死锁状态,若能,输出YES,否则输出NO
输入描述
第1行输入N(N<=10)表示数据组数。
从第2行到第N+1行,每行三个整数L,R,W。
50% L,R,W<=10^5
50% L,R,W<=10^18
输出描述
输出N行,每行输出’YES’或’NO’
解题思路
基本思想:最初的想法是进行迭代,求出每一次可读和可写的单位,只要不是同时小于R,W就可以进行继续进行读写,同时保留关于R/W的记录,当出现已经访问过的R/W说明进入循环,那么一定不会产生死锁,否则一直到不满足读写条件为止。
上述解法的问题在于对于很大的数据,迭代量可能很大,比如给出的一组测试用例:999999999999999888 633130390705049734 633130390705049735,每进行一次读写操作,会消耗一个单位,按照迭代来说需要999999999999999888 次迭代才能产生结果。
从上一个例子,又想到了能不能找到每一次变化的规律,比如,上面每一次读写消耗一个单位,规律很清晰,但是更多的测试用例是没有这样明显的规律的。
换一个角度想,如果L是无限大的,是不会存在死锁的现象的,那么也就是说L越大越不容易产生死锁,也就是说对于指定的W,R应该存在一个临界值,超过这个值就不会产生死锁。那么问题就规约到对于指定的W,R,找到这样一个临界值。
下面我们不考虑L的长度,来模拟读写的过程。按照题目要求,我们总是先进行写,然后进行读,每一次写读之后,就会消耗一定的单位,比如当R=3,W=4的时候,只写1次,那么只能读1次,读之后还剩下一个可读单位,也就是现在缓冲区消耗了一个单位,写2次的时候,能够读2次,消耗2个单位,写3次的时候,能够读3次,消耗0个单位,写4次的时候,读4次,消耗1个单位,写5次的时候,读5次,消耗2个单位,我们发现这个过程一直循环,而在这个过程中,最多消耗缓冲区的单位是2。
再比如R=10,W=4的时候,写1次的时候无法读,写2次的时候无法读,写3次的时候读1次,消耗2个单位,写4次读1次消耗6个单位。。。。写7次的时候读2次消耗8个单位,写8次读3次消耗2个单位,进入循环,这个过程最多消耗8个单位。
当考虑L的时候,模拟写读的过程。最初可读和可写的量分别时0,L,写一次之后,可读的量是sW,可写的量是L-sW,想要继续进行读的动作,就需要L-L%W>=R,读完之后,可读可写的量分别是sW-nR,L-sW+nR。而如果想要继续进行写,必须满足L-sW+nR>=W,也就是L>=W+sW-nR,其中sW-nR就是上面那个模拟过程中消耗的缓冲区的量consume,写完之后,再进行读的动作要满足L-L%W>=(n+1)R,所以一共需要满足两个条件,L>=W+max(consume),L-L%W>=R,其中max(consume)就是上面模拟的时候找到的最大消耗单位,那么下面问题就归约为寻找W,R的最大消耗单位,其实也就是R-最大公约数(W,R),可以这样理解,每一次写读之后都会消耗一定的长度,而消耗的最大长度为length,这可以认为是处理消息队列的瓶颈,只要这个时候还能继续写读,就一定不会产生死锁,所以L的最小单位是length+W。
代码实现
#include <stdio.h>
#include <vector>
using namespace std;
unsigned long long L,R,W,RR,WR;
unsigned long long measure(){
unsigned long long x,y,z;
if(W>=R){
x=W;
y=R;
}else{
x=R;
y=W;
}
z=y;
while(x%y!=0){
z=x%y;
x=y;
y=z;
}
return z;
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%llu%llu%llu",&L,&R,&W);
if(R==0 && W==0)
printf("NO\n");
else if(R!=0 && W!=0){
unsigned long long remains=L%W;
if(L-remains<R) printf("YES\n");
else{
unsigned long long gcd=measure();
unsigned long long d=R-gcd;
if(L>=W+d)
printf("NO\n");
else
printf("YES\n");
}
}else
printf("NO\n");
}
return 0;
}
按照我的理解,当RW中有一个为0另一个不为0的时候应该是会发生死锁的,但是在牛客网的测试用例中却将这两种情况判断为不会产生死锁,为了通过测试我把分支条件的最后一个改成了NO。我觉得的这是一个Bug。