并行程序模拟(Concurrency Simulator,ACM/ICPC World Finals 1991,UVa210)题解

Dear 丶 2022-12-28 07:21 224阅读 0赞

文章目录

  • 题目描述
    • 输入
    • 输出
  • 原题(PDF)
    • 案例分析
    • 算法分析
    • 解题标程
    • 错题总结

题目描述

你的任务是模拟n个程序(按输入顺序编号为1~n)的并行执行。
每个程序包含不超过25条语句,格式一共有5种:var=constant(赋值);print var(打印);lock;unlock;end。

变量用单个小写字母表示,初始值为0,为所有程序公有(因此在一个程序里对某个变量赋值可能会影响到另一个程序)。常数是小于100的非负整数。

每个时刻只能有一个程序处于运行态,其他程序均处于等待。上述五种语句分别需要t1、t2、t3、t4、t5单位时间。运行态的程序每次最多运行Q个单位时间(称为配额)。当一个程序的配额用完之后,把当前语句(如果存在)执行完之后该程序会被插入一个等待队列中,然后处理器从队首取出一个程序继续执行。

初始等待队列包含按输入顺序排列的各个程序,但由于lock和unlock语句的出现,这个序列可能会改变。 lock的作用是申请对所有变量的独占访问。lock和unlock总是成对出现,并且不会嵌套。lock总是在unlock的前面。当一个程序成功执行完lock指令后,其他程序一旦试图执行lock指令,就会马上被放到一个所谓的阻止队列的尾部(没有用完的配额就浪费了),当unlock指令执行完毕后,阻止队列的第一个程序进入等待队列的首部。

有多组输入输出数据,输入kase,n、t1、t2、t3、t4、t5、Q,按照时间顺序输出所有print语句的程序编号和结果。

输入

  1. 1
  2. 3 1 1 1 1 1 1
  3. a = 4
  4. print a
  5. lock
  6. b = 9
  7. print b
  8. unlock
  9. print b
  10. end
  11. a = 3
  12. print a
  13. lock
  14. b = 8
  15. print b
  16. unlock
  17. print b
  18. end
  19. b = 5
  20. a = 17
  21. print a
  22. print b
  23. lock
  24. b = 21
  25. print b
  26. unlock
  27. print b
  28. end

输出

  1. 1: 3
  2. 2: 3
  3. 3: 17
  4. 3: 9
  5. 1: 9
  6. 1: 9
  7. 2: 8
  8. 2: 8
  9. 3: 21
  10. 3: 21

原题(PDF)

1


案例分析

根据输入数据可知:
一组数据,三个程序块。
赋值”=”,输出”print”,封锁”lock”,解锁”unlock”,结束”end”,五个指令的运行时间为1,1,1,1,1
而整个程序的配额为1。
因此可以看出每一个程序块最多只能执行一个语句。
由此可以得出此过程:

第一程序块 “a = 4”
第二程序块 “a = 3”
第三程序块 “b = 5”
第一程序块 “print a”

这才得到第一个1: 3。

那么输出格式很简单的可以看出就是程序块的次序加上输出语句,第一个程序块,输出a的值为3

算法分析

根据题意可以看出,有两个队列需要我们去维护:等待队列和封存队列。

  • 等待队列即要在首部放入元素,又要在尾部,因此需要用一个 “双端队列”进行维护(即STL中的deque容器)
  • 封存队列,因为lock的存在,同时只有在第二次碰到lock时,需要在封存队列的尾部放入元素,因此封存队列可以为普通的队列(即STL中的queue容器)

注意:

  • 测试数据中可以会出现26个小写字母都被赋值的情况,因此要想办法存这26个小写字母所对应的值,可以使用word[26]做一个表格来维护。
  • 等待队列中存放的应该是程序块的编号,同时封存队列中应该存的也是程序块的编号,而命令我们需要将所有命令按照程序块的编号进行分类存放,每一块的命令存到一起。
  • 还要用一个标志来记录lock的状态,应对lock和unlock的不同状态
  • 这里我们用命令串的第3个字符作为标志符号:赋值”=”,输出”i”,封锁”c”,解锁”l”

解题标程

  1. #include <iostream>
  2. #include <queue>
  3. #include <vector>
  4. #include <cstdio>
  5. #include <string>
  6. #include <cstring>
  7. using namespace std;
  8. int n,t1,t2,t3,t4,t5,q; //输入
  9. deque<int> wait1; //等待队列
  10. queue<int> stop1; //封存队列
  11. vector<string> cmd[1005]; //命令
  12. bool lock1=false; //记录封存情况
  13. int t[5]; //记录运行时间
  14. int now[1005]; //当前程序运行的位置
  15. int word[26]; //各字母存的数值
  16. //输入函数
  17. void input()
  18. {
  19. scanf("%d",&n);
  20. for(int i=0;i<5;i++)
  21. scanf("%d",&t[i]);
  22. scanf("%d",&q);
  23. }
  24. //处理程序块函数
  25. void solve(int i)
  26. {
  27. int left=q; //记录剩余的配额
  28. string ss;
  29. while(left>0){
  30. //如果配额小于等于零就证明无法再执行任何的操作
  31. ss=cmd[i][now[i]]; //ss串中存放上一次执行到的命令串(第一次就是0号命令串)
  32. //赋值(值只会是一位数或者两位数)
  33. if(ss[2]=='='){
  34. int v;
  35. v=ss[4]-'0'; //一位数的情况
  36. if(ss.size()==6) v=(ss[4]-'0')*10+ss[5]-'0'; //两位数的情况
  37. word[ss[0]-'a']=v; //在word中将字母和值相对应
  38. left-=t[0]; //求出剩余的配额
  39. }
  40. //输出"print"
  41. else if(ss[2]=='i'){
  42. printf("%d: %d\n",i,word[ss[6]-'a']);
  43. left-=t[1]; //求出剩余的配额
  44. }
  45. //封锁"lock"
  46. else if(ss[2]=='c'){
  47. left-=t[2]; //求出剩余的配额
  48. //只有当封锁状态时,再执行"lock",就会把这个程序块放到阻止队列的尾部
  49. if(lock1){
  50. stop1.push(i); //把这个程序块放到阻止队列的尾部
  51. return; //该程序直接结束,进入下一程序
  52. }
  53. else lock1=true; //若是解锁状态就将状态改为封锁
  54. }
  55. //解锁"unlock"
  56. else if(ss[2]=='l'){
  57. left-=t[3]; //求出剩余的配额
  58. lock1=false; //解锁
  59. if(!stop1.empty()){
  60. //将封闭队列的首位元素放到等待队列的首位
  61. int k;
  62. k=stop1.front(); //取出封存队列的首位元素
  63. stop1.pop(); //删除封存队列首位元素
  64. wait1.push_front(k); //将封存队列的首位元素放到等待数列的首位
  65. }
  66. }
  67. //结束"end"
  68. else
  69. return;
  70. ++now[i]; //最后还要将程序块中的命令串的行数下移,使得程序块执行下一个指令
  71. }
  72. wait1.push_back(i); //循环进行,这个程序块完了再插到尾部
  73. }
  74. int main(int argc, const char * argv[]) {
  75. int kase; //多组案例
  76. scanf("%d",&kase);
  77. while(kase--){
  78. input(); //输入
  79. //对每一块程序块下的命令进行分块存放
  80. string s;
  81. for(int i=1;i<=n;i++){
  82. cmd[i].clear();
  83. while(getline(cin, s)){
  84. //整行读入(可以读入空格)
  85. if(s=="") continue; //样例中会出现""这种情况需要单独处理
  86. cmd[i].push_back(s); //cmd[i]中存放的就是第i个程序块中的命令
  87. if(cmd[i].back()=="end") break; //当存到"end"就退出
  88. }
  89. wait1.push_back(i); //一开始的程序块就是顺序存放
  90. }
  91. memset(now, 0, sizeof(now));
  92. memset(word,0,sizeof(word));
  93. while(!wait1.empty()){
  94. //等待队列不为空就循环
  95. //取出等待队列的首元素去执行“命令”
  96. int k=wait1.front();
  97. wait1.pop_front(); //删除首位元素
  98. solve(k);
  99. }
  100. if(kase) puts(""); //题目案例bug
  101. }
  102. return 0;
  103. }

错题总结

  • cpp中的string可以用getline(cin,s)直接读入整行的字符串(可以读入空格)
  • deque“双端队列”和queue“普通队列”以及动态数组“vector”的使用方法

发表评论

表情:
评论列表 (有 0 条评论,224人围观)

还没有评论,来说两句吧...

相关阅读