Uva #690 Pipeline Scheduling (习题7-5)

前端之家收集整理的这篇文章主要介绍了Uva #690 Pipeline Scheduling (习题7-5)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

原题读起来感觉有一点绕,不过题意很简单:其实就是计算机要重复地做一些相同的任务,每个任务需要一定时间,在这段时间内可能会用到不同的处理器(共有5个处理器)。一个处理器在使用的时候会锁住,其他的任务就不能访问了。只能等到下一个clock cycle再使用这个处理器。

任务使用处理器的pattern就是input,比如


7
X...XX.
.X.....
..X....
...X...
......X


就代表每个任务耗时7个cycle,X表示在这一个cycle内这个处理器会被使用。

问题是:现在有10个这样的任务,如何安排他们使得计算机在最短时间内处理完所有任务。每个任务耗时小于20个cycle。输出最短的时间。



理解了题意之后,可以大概得出一个解法,想法并不难(但是要AC还是有点难度。。):

回溯法dfs,模拟八皇后问题,每次尝试在下一个位置“放置”一个任务,看看会不会和之前的任务有冲突。


但是这道题的难点是,需要得出所有可能的结果,最后选出一个最小的。但是可能的结果是无穷多的(因为只要不冲突,可以往后面的任意位置摆放),一点点分析可以得出答案应小于200(即任务之间完全没有重叠)。但是解答树的节点还是太多了,如果不剪枝,最坏情况下结点数的规模大概是20^10(每个任务最多有20种摆放的位置,共有10个任务),会TLE


剪枝的质量会非常显著的影响程序的效率。另外,剪枝的时候要小心,不要错剪,一定要进行证明,说服自己。


1、最重要的剪枝:从当前结点开始,每次都走最小步数(最小步数就是摆放第二个任务时可以走得最小步数,(Rujia: 想一想,为什么?))如果这样到最后的结果还要大于目前的最优解,则剪枝。剪枝的式子一定要算准确,剪少了跑的慢或者TLE,剪多了WA(而且往往是大部分答案正确,小部分答案错误,导致很难察觉)

没有这个剪枝,程序几乎是完全没法跑


2、第二重要的剪枝:预判一下20个摆放位置的可能性。在尝试摆放的时候,20个位置中,并不是每个位置都可以,有一些位置是不管在什么情况下都不能摆放的-即第二个任务不能摆放的位置。将这些位置剔除,保留下有可能性的位置,程序性能可以提高几倍(在我的测试中是5倍)。这个优化很像Morning after Halloween中,邻接图转为邻接表的优化。


3、一个重要的优化:借鉴了BearChild大神的思路:http://www.jb51.cc/article/p-zsdqnugi-vy.html

我一开始用了一个大vis数组来判断冲突,这个数组需要开到5*200。所以没法使用二进制表达。但是BearChild大神很巧妙的避免了这个大vis,转而在每一次尝试摆放一个任务的时候,做一个小的vis,其中存放了只会对当前任务的摆放产生影响的点。规模就变成了5*20,从而可以使用5个int作二进制表达而增加效率。这里还可以训练一下二进制操作,是个不错的练习


通过最近暴力法的练习,很强烈的感受到剪枝的重要性和技巧性。剪枝往往直接决定着程序能不能跑起来、能不能得出正确答案



Run Time: 0.235s

#define UVa  "7-5.690.cpp"

#include<cstring>
#include<cstdio>
#include<string>
#include<vector>

using namespace std;

//Global Variables. Reset upon Each Case!
int n;
int reserve[5];
int ans;
vector<int> available_steps;
/////

int judge(int* vis,int offset) {
    for(int i = 0; i < 5; i ++) if(reserve[i]&(vis[i]>>offset)) return 0;
    return 1;
}

int init() {
    ans = 200;
    available_steps.clear();
    memset(reserve,sizeof(reserve));

    char ch;
    for(int i = 0; i < 5; i ++) {
        for(int j = 0; j < n; j ++) {
            do{scanf("%c",&ch);}while(!isprint(ch));
            if(ch == 'X') reserve[i] |= (1<<j);
        }
    }
    for(int i = 1; i <= n; i ++) {
        if(judge(reserve,i)) {
            available_steps.push_back(i);
        }
    }
}


int dfs(int taskd,int start,int* window) {
    if(taskd == 9) {
        if(start + n < ans) ans = start + n;
    }
    else {
        for(int i = 0; i < available_steps.size(); i ++) {
            if(start + available_steps[i] + (9-taskd-1)*available_steps[0] + n > ans) return 0;           //key pruning.

            if(judge(window,available_steps[i])) {
                int tmp[5];
                memcpy(tmp,window,sizeof(tmp));
                for(int j = 0; j < 5; j ++) tmp[j] = (tmp[j]>>(available_steps[i])) | reserve[j];
                dfs(taskd + 1,start + available_steps[i],tmp);
            }
        }
    }
}
int main() {
    while(scanf("%d",&n) != EOF && n) {
        init();
        dfs(0,reserve);
        printf("%d\n",ans);
    }
    return 0;
}








猜你在找的设计模式相关文章