UVa 690 Pipeline Scheduling 流水线调度 二进制表示状态 DFS 剪枝

题目链接:Pipeline Scheduling
题目描述:

给定一张 5 × n ( 1 ≤ n ≤ 20 ) 5\times n(1\le n\le20) 5×n(1n20)的资源需求表,第 i i i行第 j j j列的值为’X’表示进程在 j j j时刻需要使用使用资源 i i i,如果为’.'则表示不需要使用。你的任务是安排十个相同进程的启动时间,使得所有进程完成的时间尽可能的早。在安排每个进程的开始时间你需要注意:

  • 一个进程一旦开始就必须要一直执行,而不能停止;
  • 任意一个资源必须进行互斥使用,也就是任意时刻 j j j需要保证对于 1 ≤ i ≤ 5 1\le i\le5 1i5 i i i资源最多只被一个进程所使用。

你需要输出最早的完成时间。
例如输入为:
在这里插入图片描述
对应的输出应该是 34 34 34,对应的每个进程的执行情况如下表(图中的编号代表进程的编号):
在这里插入图片描述

题解:

本题不难想到的暴力方法是,枚举十个进程的开始时间,由于一共有十个进程每个进程开始的时间范围为: [ 0 , n ] [0, n] [0,n],所以时间复杂度为 O ( n 10 ) O(n^{10}) O(n10)
这样需要枚举的状态太多了,如何减少状态呢?在我们已知每个资源关于时间的需求情况实际上我们可以知道并不是 [ 0 , n ] [0, n] [0,n]所有的开始时间都是可行的,我们能够确定出有限个可行的开始时间。我们设当前进程相对于上一个进程晚开始 d e l a y T i m e delayTime delayTime,那么此时当前的进程会在那些时刻占用那些资源是可以确定的,而只有当前进程占用的资源与上一个进程占用的资源不存在冲突的时候 d e l a y T i m e delayTime delayTime才是可行的,如果能够提前计算出所有的 d e l a y T i m e delayTime delayTime,那么最后进行枚举的时候复杂度就会大大减少。
如何快速判断一个 d e l a y T i m e delayTime delayTime是否可行?我们可以用二进制来保存每个资源与时间的关系,例如样例的输入我们可以用二进制表示为:
s t a t u s [ 0 ] = 0110001 s t a t u s [ 1 ] = 0000010 s t a t u s [ 2 ] = 0000100 s t a t u s [ 3 ] = 0001000 s t a t u s [ 4 ] = 1000000 status[0] = 0110001\\ status[1] = 0000010\\ status[2] = 0000100\\ status[3] = 0001000\\ status[4] = 1000000 status[0]=0110001status[1]=0000010status[2]=0000100status[3]=0001000status[4]=1000000
而当前进程相对于上一个进程延迟 d e l a y T i m e delayTime delayTime后开始,那么上一个进程在当前进程的各个资源占用会产生影响的部分实际上是 s t a t u s [ i ] > > d e l a y T i m e status[i] >> delayTime status[i]>>delayTime(注意这里使用的二进制表示和图上是颠倒的,所以此处需要用右移表示时间的流逝,而如果不颠倒处理需要进行特定的位数判断比较麻烦),而当前进程的资源使用情况就是 s t a t u s [ i ] status[i] status[i],所以只要KaTeX parse error: Expected 'EOF', got '&' at position 11: status[i] &̲ (status[i] >>d…就代表资源 i i i不会发生冲突,只有五个资源都不发生冲突的 d e l a y T i m e delayTime delayTime才是可行的。
还有剪枝方法吗?实际上这样应该能够通过大部分数据,但是我们还有一个比较简单的剪枝,但是这个剪枝能够去除掉很多的状态。我们需要记录一个 m i n D e l a y T i m e minDelayTime minDelayTime表示所有 d e l a y T i m e delayTime delayTime中的最小值,如果 n o w S t a r t T i m e + r e s t P r o c e s s N u m × m i n D e l a y T i m e + n ≥ n o w A n s nowStartTime + restProcessNum \times minDelayTime + n \ge nowAns nowStartTime+restProcessNum×minDelayTime+nnowAns,那么我们直接进行剪枝即可(即假设后续所有的进程都能最早开始但是依然不能早于当前记录的答案时进行减值)。

代码:

#include <bits/stdc++.h>

const int INF = 0x3f3f3f3f;
const int UNIT_NUM = 5;
const int PROCESS_NUM = 10;

using namespace std;

int ans, n, minDelayTime;
int status[UNIT_NUM];
string reservationTable;
vector<int> nextStep;

void init()
{
    nextStep.resize(0);
    ans = INF;
    minDelayTime = -1;
    for (int delayTime = 1; delayTime <= n; delayTime++) {
        bool canStart = true;
        for (int unitID = 0; unitID < UNIT_NUM; unitID++) {
            if ((status[unitID] >> delayTime) & status[unitID]) {
                canStart = false;
                break;
            }
        }
        if (canStart) {
            if (minDelayTime == -1) { minDelayTime = delayTime; }
            nextStep.push_back(delayTime);
        }
    }
}

void dfs(int nowDepth, int nowStartTime, int s0, int s1, int s2, int s3, int s4) {
    if (nowDepth == PROCESS_NUM - 1) { // 这里等于PROCESS_NUM - 1是因为0号进程已经安排到0时刻开始,后续安排的是1-9号进程
        ans = min(ans, nowStartTime + n);
        return ;
    }
    if (nowStartTime + (9 - nowDepth) * minDelayTime + n >= ans) { return; }
    for (int delayTime : nextStep) {
        int ns0 = s0 >> delayTime, ns1 = s1 >> delayTime, ns2 = s2 >> delayTime, ns3 = s3 >> delayTime, ns4 = s4 >> delayTime;
        if ((ns0 & status[0]) || (ns1 & status[1]) || (ns2 & status[2]) || (ns3 & status[3]) || (ns4 & status[4])) {continue; }
        dfs(nowDepth + 1, nowStartTime + delayTime, ns0 | status[0], ns1 | status[1], ns2 | status[2], ns3 | status[3], ns4 | status[4]);
    }
}

int main()
{
    ios::sync_with_stdio(false);
    while (cin >> n && n != 0) {
        for (int i = 0; i < UNIT_NUM; i++) {
            cin >> reservationTable;
            status[i] = 0;
            for (int j = 0; j < n; j++) {
                if (reservationTable[j] == 'X') {
                    status[i] |= 1 << j;
                }
            }
        }
        init();
        dfs(0, 0, status[0], status[1], status[2], status[3], status[4]);
        cout << ans << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值