考虑第一个程序运行时哪些工作单元是在工作中的,上面说过对于下一个单元它只能在
s
+
1
s + 1
s+1到
s
+
n
+
1
s + n + 1
s+n+1内开始,但是并不是所有时间都可以开始的,它下一个能开始运行的时间是具有跳跃性的,就比如说题中的样例,他就只能当前程序运行的第2、3、6、7的时间开始,别的时间开始都会有冲突,对于后面的所有程序都是只能在上一个程序的第2、3、6、7的时间开始
把跳跃的时间用数组jump[cnt]记录下来,这样的好处就是可以少枚举一些不必要的程序开始时间,
c
n
t
cnt
cnt是可跳跃的个数
还有一个好处就是可以大大地剪枝,具体的剪枝操作是:假如当前递归到了第
n
u
m
num
num个数字,上一个程序的开始时间是
s
s
s,则最少还要
s
+
j
u
m
p
[
0
]
×
(
10
−
n
u
m
)
+
n
s + jump[0] \times (10 - num) + n
s+jump[0]×(10−num)+n的时间完成10个程序运行的时间,这样就可以和先前算好的
M
i
n
Min
Min进行对比剪枝了
优化好的代码如下,在电脑上运行20个
n
=
19
n = 19
n=19的不同数据都能很快地出答案,但很可惜,交上去还是tle了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, jump[25], Min, cnt = 0;
char unit[6][25];
bool run[5][500];
bool check(int s) {
for (int i = 0; i < n; i++)
for (int j = 0; j < 5; j++)
if (unit[j][i] == 'X' && run[j][i + s]) return false;
return true;
}
void dfs(int num, int s) {
if (s + n >= Min) return;
if (s + jump[0] * (10 - num) + n >= Min) return;
if (num == 10) { Min = s + n; return; }
for (int i = 0; i < cnt; i++) {
if (!check(s + jump[i])) continue;
for (int j = 0; j < 5; j++)
for (int k = 0; k < n; k++)
if (unit[j][k] == 'X') run[j][s + jump[i] + k] = true;
dfs(num + 1, s + jump[i]);
for (int j = 0; j < 5; j++)
for (int k = 0; k < n; k++)
if (unit[j][k] == 'X') run[j][s + jump[i] + k] = false;
}
}
inline void init(){
memset(run, 0, sizeof run);
cnt = 0;
Min = 0xffff;
}
int main () {
#ifndef ONLINE_JUDGE
freopen("D:/MYCODE/vsCode-c/test.in", "r", stdin);
freopen("D:/MYCODE/vsCode-c/test.out", "w", stdout);
#endif
while (scanf("%d", &n), n) {
for (int i = 0; i < 5; i++) { scanf("%s", unit[i]); }
init();
for (int i = 0; i < n; i++)
for (int j = 0; j < 5; j++)
if (unit[j][i] == 'X') run[j][i] = true;
for (int i = 1; i <= n; i++) if(check(i)) jump[cnt++] = i;
dfs(1, 0);
printf("%d\n", Min);
}
return 0;
}