附上题目连接~concurrency simulator
本题为紫书数据结构基础篇第一道例题,是一道考察双端队列的模拟题,由于使用了STL,题目的难度和编程量大大降了下来,不过本菜鸟还是花了三个半小时才拿下了这道题,30msAC,可想见代码有多烂。
这是第一次做模拟题,第一感受是信息量有些大,不过仔细梳理后发现不过就几个点:模拟的主体是几个程序,程序由不超过25条5种类型的语句组成,每条语句都有固定的运行时间;使用等待队列和阻止队列管理程序,每个程序每次出队都有一个固定配额;理解lock和unlock语句的规则;当然还有共享的不超过26个的变量。
理解了题目接下来就是选取恰当的数据结构存储数据,并且编写输入和输出代码。这一部分是本菜鸟最头痛的部分,每次都固定带走一个半小时,但我相信这样的花费是值得的,因为一个程序的好坏甚至能否成功运行至少有一半得归结于如何将给定输入信息
转化为程序信息
,以及选择适当的数据结构方便信息的获取和检索
。下面来说说我的思路:
可以看出,主要要处理的输入信息为程序的语句,首先可以看到语句中有许多不可控的空格
,我们要想办法将它们剔除;其次计算机是不会认识这些语句的,我们需要让计算机认识它们,最直接的想法是每次将语句做程序中自己加入的字符串比较,但是这样会造成时空上的极大浪费,于是我们想到用单字符或数字
来对语句加以区分,只需要检索或存储其关键信息
即可。通过观察,程序五条语句的首字母各不相同,我们可以将其做为区分标识
,但是由于变量名又是小写的字母,所以我们需要对语句的第二个字符做一个判断,如果是’=’,则语句为赋值语句,第一个字母为变量名,否则即为其他语句。
这样输入信息就处理好了,接下来是如何存储。程序运行的固定配额、程序数以及每条语句运行时间很简单,我们只需分别用int型的q, num, t[5]存储,由于题目中的变量是多个程序共有,我们也可以用int var[maxn]方便的存储各个变量的值。最后就是‘程序’的存储,明显的有两种选择,建立规则将信息处理后再存储,或者直接存储使用时再处理,后者显然很蠢,但是菜鸟还是脑子一抽就开了个char型三维数组存下来了(哎,还是因为前面一直纠结于用什么办法接受输入并且如何处理,查了一堆C++函数觉得太烦 了,干脆上万能的getchar(),连处理也不怎么处理了,剔一些空格完事;至于为什么不一开始就用这么省力的方法,就是觉得这样做好low,而且用了这么久还是对scanf的读入规则还是稀里糊涂,对到底哪些地方需要添加(几个)getchar()接收缓冲区的换行和空白符不怎么清楚,下来一定要好好学习整理一下)
最后就是程序的主体,不过是简单的翻译,有了stl实现了双端队列编写起来就很简单。需要一提的是,一般模拟题不可或缺的状态参量的定义,对应到本题就是index[maxn]
和blocked
分别记录每个程序运行到第几句
(相当于真实的PC指针)以及是否处于独占变量状态
(注意,题目对这一问题做了简化,即便是一个程序独占了变量,其他程序还是可以对变量进行修改的,但是本菜鸟却以为这一简化反到造成了题目理解的一些障碍)。
最后需要注意的一点就是,每次执行完一个样例后需要恢复到初始状态,即所有相关变量内容的归零,菜鸟常常这里忘记了造成后面灾难性的调试,这次也不例外。一个比较好的解决办法是将相关语句提取出来单独定义一个init()函数
,每次run之前调用一下恢复状态,这样程序维护起来也方便。希望以后能慢慢养成这个习惯吧。还有一些标志符
和特殊值
的细节问题,以后再一一总结吧。
ps:菜鸟深知自己的变量命名水平实在捉急,请大神勿喷。
#include
#include
#include
#include
#define maxn 30
using namespace std;
char program[maxn][maxn][maxn];
int q, t[5], var[maxn], num;
int value(char* integer)
{
int i = 0, outcome = 0;
while (integer[i] != '\0')
{
outcome *= 10;
outcome += integer[i] - '0';
i++;
}
return outcome;
}
void run()
{
deque<int> q1, q2;
for (int i = 0; i < num; i++)
q1.push_back(i);
memset(var, 0, sizeof(var));
int index[maxn] = { 0 },blocked = 0; // 状态参量,index为pc指针,blocked为是否搜定
while (!q1.empty())
{
int temp = q1.front(), r = q, finished = 0;
q1.pop_front();
while (r > 0 && !finished)
{
char c = program[temp][index[temp]][0];
if (program[temp][index[temp]][1] != '=')
switch (c)
{
case 'p':
printf("%d: %d\n", temp + 1, var[program[temp][index[temp]][5] - 'a']);
r -= t[1];
index[temp]++;
break;
case 'l':
if (blocked)
{
q2.push_back(temp);
r = -10000000;//特殊值用于后面是否入队判断,注意和正常可能取值区分!!-1也是正常的
}
else
{
blocked = 1;
r -= t[2];
index[temp]++;
}
break;
case 'u':
blocked = 0;
if (!q2.empty())
{
int p = q2.front();
q2.pop_front();
q1.push_front(p);
}
r -= t[3];
index[temp]++;
break;
case 'e':
r = 0;
finished = 1;
break;
}
else
{
var[c - 'a'] = value(program[temp][index[temp]]+2);
r -= t[0];
index[temp]++;
}
}
if (!finished && r!=-10000000) q1.push_back(temp);
}
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &num);
for(int i = 0; i < 5; i++)
scanf("%d", &t[i]);
scanf("%d", &q);
getchar();
for (int i = 0; i < num; i++)
{
char temp[maxn];
int j = 0;
while (1)
{
char c;
int k = 0;
while ((c = getchar()) != '\n')
temp[k++] = c;
temp[k] = '\0';
if (strcmp(temp,"end") == 0)
{
strcpy(program[i][j], temp);
break;
}
int p1 = -1, p2 = 0;
while (temp[++p1] != '\0')
if (temp[p1] != ' ')
program[i][j][p2++] = temp[p1];
program[i][j][p2] = '\0';
j++;
}
}
run();
if(T) puts("");
}
}
下面附上刘汝佳大神的代码
// UVa210 Concurrency Simulator
// Rujia Liu
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1000;
deque<int> readyQ;
queue<int> blockQ;
int n, quantum, c[5], var[26], ip[maxn]; // ip[pid]是程序pid的当前行号。所有程序都存在prog数组,更类似真实的情况,代码也更短
bool locked;
char prog[maxn][10];
void run(int pid) {
int q = quantum;
while(q > 0) {
char *p = prog[ip[pid]];
switch(p[2]) {
case '=':
var[p[0] - 'a'] = isdigit(p[5]) ? (p[4] - '0') * 10 + p[5] - '0' : p[4] - '0';
q -= c[0];
break;
case 'i': // print
printf("%d: %d\n", pid+1, var[p[6] - 'a']);
q -= c[1];
break;
case 'c': // lock
if(locked) { blockQ.push(pid); return; }
locked = true;
q -= c[2];
break;
case 'l': // unlock
locked = false;
if(!blockQ.empty()) {
int pid2 = blockQ.front(); blockQ.pop();
readyQ.push_front(pid2);
}
q -= c[3];
break;
case 'd': // end
return;
}
ip[pid]++;
}
readyQ.push_back(pid);
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d %d %d %d %d %d %d\n", &n, &c[0], &c[1], &c[2], &c[3], &c[4], &quantum);
memset(var, 0, sizeof(var));
int line = 0;
for(int i = 0; i < n; i++) {
fgets(prog[line++], maxn, stdin);
ip[i] = line - 1;
while(prog[line - 1][2] != 'd')
fgets(prog[line++], maxn, stdin);
readyQ.push_back(i);
}
locked = false;
while(!readyQ.empty()) {
int pid = readyQ.front(); readyQ.pop_front();
run(pid);
}
if(T) printf("\n");
}
return 0;
}
lrj原来是用fgets来处理输入的,而且连空格都不去除,直接默认已知空格在哪些位置会出现,并用第三个字符来标识五种语句。
最后附上我事后查阅的一些资料
最后一篇博文,该博主对双端队列问题做了详细的总结,代码部分听过某一老师讲解,有较详细注释,可以参考。
ps:程序变量的命名还是重要啊,提交代码过程中WA了一个compilation error,查看详细信息后发现能报WA这个错的我也是人才一个。。