算法竞赛入门经典(第二版)-刘汝佳-第六章 数据结构基础 例题(18/22)

文章目录

  • 说明
  • 例题
    • 例6-1 UVA 210 并行程序模拟 (p139, 双端队列)
    • 例6-2 UVA 514 铁轨 (p140, 栈)
    • 例6-3 UVA 442 矩阵链乘 (p141, 用栈实现简单的表达式解析)
    • 例6-4 UVA 11988 破损的键盘 (p143, 链表)
    • 例6-5 UVA 12657 移动盒子 (p144, 双向链表)
    • 例6-6 UVA 679 小球下落 (p148, 完全二叉树编号)
    • 例6-7 UVA 122 树的层次遍历 (p150, 二叉树的动态创建于 BFS)
    • 例6-8 UVA 548 树 (p155, 从中序和后续回复二叉树)
    • 例6-9 UVA 839 天平 (p157, 二叉树的 DFS)
    • 例6-10 UVA 699 下落的树叶 (p159, 二叉树的 DFS)
    • 例6-11 UVA 297 四分树 (p160)
    • 例6-12 UVA 572 油田 (p162, 图的连通块 DFS)
    • 例6-13 UVA 1103 古代象形符号 (p163, 图的连通块的应用)
    • 例6-14 UVA 816 Abbott 的复仇 (p165, 图的连通块 BFS)
    • 例6-15 UVA 10305 按任务排序 (p167, 拓扑排序)
    • 例6-16 UVA 10129 单词 (p169, 欧拉回路)
    • 例6-17 UVA 10562 看图写数 (p170, 多叉数 DFS)
    • 例6-18 UVA 12171 雕塑 (p171, 离散化: floodfill)(未尝试)
    • 例6-19 UVA 1572 自组合 (p172, 图论模型)
    • 例6-20 UVA 1599 理想路径 (p173, 图的 BFS 树)(未尝试)
    • 例6-21 UVA 506 系统依赖 (p173, 图的概念与拓扑排序)(未尝试)
    • 例6-22 UVA 11853 战场 (p175, 对偶图)(未尝试)

说明

本文是我对第六章22道例题的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。
另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第六章例题contest
如果想直接看某道题,请点开目录后点开相应的题目!!!

例题

例6-1 UVA 210 并行程序模拟 (p139, 双端队列)

思路
这个题做了得有五六个小时吧,RE了3次,WA了一次。这种模拟题总感觉在理解上有歧义,也许有人一上来就理解对了,但我上来就没理解对(也可能因为原题描述不清楚),于是就一直没有正确的思路。

书中给出的题意是这样:

你的任务是模拟n个程序(按输入顺序编号为1~n)的并行执行。每个程序包含不超过25条语句,格式一共有5种:var = constant(赋值);print var(打印);lock;unlock;end。
变量用单个小写字母表示,初始为0,为所有程序公有(因此在一个程序里对某个变量赋值可能会影响另一个程序)。常数是小于100的非负整数。
每个时刻只能有一个程序处于运行态,其他程序均处于等待态。上述5种语句分别需要t1、t2、t3、t4、t5单位时间。运行态的程序每次最多运行Q个单位时间(称为配额)。当一个程序的配额用完之后,把当前语句(如果存在)执行完之后该程序会被插入一个等待队列中,然后处理器从队首取出一个程序继续执行。初始等待队列包含按输入顺序排列的各个程序,但由于lock/unlock语句的出现,这个顺序可能会改变。
lock的作用是申请对所有变量的独占访问。lock和unlock总是成对出现,并且不会嵌套。lock总是在unlock的前面。当一个程序成功执行完lock指令之后,其他程序一旦试图执行lock指令,就会马上被放到一个所谓的阻止队列的尾部(没有用完的配额就浪费了)。当unlock执行完毕后,阻止队列的第一个程序进入等待队列的首部。
输入n, t1, t2, t3, t4, t5, Q以及n个程序,按照时间顺序输出所有print语句的程序编号和结果。

我对题意补充一些说明:

1、变量始终为所有程序公有,根本没有某个程序对于变量的私有化处理,也就是说所谓的独占访问跟变量公有不矛盾。
2、unlock会使整个任务状态解除锁定。
3、执行完lock指令之后其他程序试图执行lock指令时,这个lock指令此时不会被执行。(我这个地方理解错了)

关于我编程中所犯错误的小结:

1、lock指令的处理不当,参考前面说明。
2、unlock时,阻止队列中并不一定有程序,这时应该进行判断(RE的原因)。

另外补充几个问题:

1、题目示例INPUT漏掉了测试数据组数。
2、最后一组数据后不应该输出空行。

最后,给出一组测试数据,供查错参考:
INPUT:

1

3 1 1 1 1 1 3
a = 5
print a
end
b = 7
lock
print a
print b
unlock
c = 10
print a
end
print a
print b
lock
print c
unlock
end

OUTPUT:

1: 5
2: 5
3: 5
3: 7
2: 7
3: 10
2: 5

代码

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

const int N = 1001;

int n, qt, t[5];
queue prog[N];
deque dq;
queue que;
map mp;

int cur, passed, locked;

void init_data()
{
    for (int i = 0; i < n; i++)
        while (prog[i].size()) prog[i].pop();
    while (dq.size()) dq.pop_front();
    while (que.size()) que.pop();
    mp.clear();
}

void run_prog()
{
    cur = dq.front();
    dq.pop_front();
    passed = 0;

    while (passed < qt && prog[cur].size()) {
        string s = prog[cur].front();
        //cout << cur+1 << "----------" << s << endl;
        string name, tmp;
        int val;
        if (s.find('=') != string::npos) {
            stringstream ss(s);
            ss >> name >> tmp >> val;
            mp[name] = val;
            passed += t[0];
        } else if (s.find("print") != string::npos) {
            stringstream ss(s);
            ss >> tmp >> name;
            printf("%d: %d\n", cur+1, mp[name]);
            passed += t[1];
        } else if (s == "lock") {
            if (locked) {
                que.push(cur);
                return;
            }
            locked = 1;
            passed += t[2];
        } else if (s == "unlock") {
            locked = 0;
            if (que.size()) {
                dq.push_front(que.front());
                que.pop();
            }
            passed += t[3];
        } else if (s == "end") {
            return;
        }
        prog[cur].pop();
    }
    dq.push_back(cur);
}

int main()
{
    int kase;
    scanf("%d", &kase);
    while (kase --) {
        scanf("%d", &n);
        for (int i = 0; i < 5; i++)
            scanf("%d", &t[i]);
        scanf("%d", &qt);
        getchar();
        init_data();
        string s;
        for (int i = 0; i < n; i++) {
            do {
                getline(cin, s);
                prog[i].push(s);
            } while (s != "end");
            dq.push_back(i);
        }   
            
        locked = 0;
        while (dq.size()) {
            run_prog(); 
            //printf("passed = %d, dq.size = %d\n", passed, dq.size());
        }       
            
        if (kase) printf("\n");
    }       
        
    return 0;
}

例6-2 UVA 514 铁轨 (p140, 栈)

思路
先进后出为栈,详细分析见书中例题部分。
代码

#include 
#include 
#include 
#include 
using namespace std;

const int N = 1000;

int n, a[N+1];
stack s;

int main(void)
{
    while (cin >> n && n) {
        while (cin >> a[1] && a[1]) {
            for (int i = 2; i <= n; i ++)
                scanf("%d", &a[i]);
            int i = 1, j = 1;
            while (i <= n) {
                while (!s.empty() && s.top() == a[i]) {
                    s.pop(); i ++;
                }
                if (j <= n) s.push(j++);
                else break;
            }
            if (i > n) puts("Yes");
            else puts("No");
        }
        cout << endl;
    }

    return 0;
}

例6-3 UVA 442 矩阵链乘 (p141, 用栈实现简单的表达式解析)

思路
这个题的输入表达式非常标准,两个矩阵用一对括号包起来。如下面所示:
A
B
C
(AA)
(AB)

你可能感兴趣的:(算法竞赛入门经典,uva,算法,算法竞赛入门经典,数据结构,acm)