题目名称
拯救大兵雷诺
题目描述
新晋的星灵大主教Artanis在目睹了挚友Zeratul的牺牲之后决心对抗黑暗之神Amon。为此,他首先要团结全星区所有种族的力量。
坚定的盟友雷诺和他刚刚结束了Arcturus统治的Terran帝国正在遭受Amon控制的异虫的攻击,帮助他们即可在日后对抗Amon的时候获得帮助。
当星灵部队抵达时,人类的战线已经崩溃,Artanis预测,如果没有星灵的帮助,人类部队将在n单位时间之后彻底战败。异虫部队在战场上建立了m个据点,编号从1到m,要想帮助人类取得胜利,必须击破全部的m个据点。Artanis还预测出了击破第i个据点所需的时间Ti,同时,击破第i个据点会导致异虫的攻势减弱,人类战败的倒计时会增加Ci单位时间。此外,Artanis还计算出了星灵部队战场上任意两点间移动所需的时间Ai,j表示从第i个据点到第j个据点所需的单位时间,0 <= i, j <= m,0表示星灵部队的大本营。
由于Artanis作为大主教还是个新人,指挥能力还不足以发动多线作战,因此他只能从大本营出发,一个一个地摧毁异虫的据点。他现在想知道,在保证人类部队不全军覆没的前提下,有多少种不同的进攻顺序?
输入格式
输入包括m + 4行:
第1行包括2个整数:n和m
第2行包括m个整数:Ti
第3行包括m个整数:Ci
第4至m+4行(共m+1行)是一个(m+1) * (m+1)整数矩阵:Ai,j
整数之间使用空格分隔
输出格式
输出只包含1个整数,即不同的进攻顺序方案数
输出结尾应有一个换行符
数据规模:
0 < n <= 10000, 0 < m <= 10, 0 < Ti, Ci, Ai,j <= 1000
此外,由于星灵拥有诸多人类无法理解的高端技术,因此在据点之间移动所需的时间Ai,j不一定满足三角形不等式,即不保证满足Ai,j <= Ai,k + Ak,j,也不能保证Ai,j = Aj,i,但是Ai,i一定为0
样例输入:
5 3
1 2 3
3 2 1
0 1 1 1
1 0 1 1
1 1 0 1
1 1 1 0
样例输出:
2
样例解释:
只有1 2 3和2 1 3的进攻顺序才能保证倒计时始终在0以上
如果按照1 3 2的顺序来进攻的话,进攻1需要1+1 = 2的时间,还剩下3,又增加3变成6;接下来进攻3需要1+3=4的时间,还剩下2,又增加1变成3;最后进攻2需要1+2=3的时间,倒计时变成0,人类战败。
题目难度
0
题目解释
即求遍历所有m个点且不被打败的方案数,每一次能够到下一个点前提条件是该点没有被遍历过,同时目前剩余时间能够到达这一个点并且打败这一个点。
方法&解释
利用深搜回溯,第一次进入是第一个点0,当前的剩余的时间n,和当前经过的点数tot,先判断是否tot==m,如果tot达到m则说明此方案遍历了m个点,方案数++,回退到前一个点(前一个点继续循环改变后一个点),如果tot!=m则列举当前能够走的点1-m均可以作为下一个到达的点,只需要判断即可,如果b数组用来标记该点是否被走过,走过则标记为1如果没有走过则为0,判断是否走过,目前的方案没有走过该点则可以走,另一个判断是否能够在剩余时间内打败该点,如果两个条件均满足则能够走该点,于是走该点,b[j]标记为1,走的点数加一,tot++,继续往下走,进入子程序(那么j即为进入的点,时间也应该为开始的时间-a[i][j]-t[j]+c[j]);
每一次走每一步都可以在1-m之中选择,即下面的循环,当一个方案出了之后,回退到之前的(可以以栈来模拟演示一下)子程序,回溯即b[j]标记为0,tot--相当于将该步骤走的该点置为没有走过,其实这个方法和m*m层循环并没有什么区别,都是把全部的方案都遍历,只是在之前就判断了一点是否可以进入,而不是像循环一样全部都傻傻地进行了,而且其实循环还要每一层层判断比这个难写,每一次改变方案数都是从后向前,先是最后一个点改变,所有改变完之后,然后倒数第二个点改变,接着又进入最后一步,又最后一个点改变...大致循环过程是和循环一样的,可以按照循环的改变来理解。
我的代码
1 #include<stdio.h>
2 int m, b[12] = {0}, a[12][12] = {0}, t[12] = {0}, c[12] = {0}, num = 0;
3 void does(int i, int n, int tot) {
4 int j;
5 if (tot == m) {//是否完成一个方案
6 num++;
7 return;//完成一个方案则返回
8 }
9 for (j = 1; j <= m; j++) {
10 if (b[j] == 0 && a[i][j] + t[j] < n) {//判断你目前要走的点是否可以走
11 b[j] = 1;
12 tot++;
13 does(j, n - a[i][j] - t[j] + c[j], tot);//走下一个点
14 b[j] = 0;//回溯
15 tot--;
16 }
17 }
18 }
19 int main() {
20 int n, i, j;
21 scanf("%d%d", &n, &m);
22 for (i = 1; i <= m; i++) {
23 scanf("%d", &t[i]);
24 }
25 for (i = 1; i <= m; i++) {
26 scanf("%d", &c[i]);
27 }
28 for (i = 0; i <= m; i++) {
29 for (j = 0; j <= m; j++) {
30 scanf("%d", &a[i][j]);
31 }
32 }
33 b[0] = 1;
34 does(0, n, 0);//从零点出发
35 printf("%d\n", num);
36 }
标程代码
1 #include <stdio.h>
2
3 #define MAXm 11
4
5 typedef struct {
6 int x, i, n;
7 } str;
8
9 int main() {
10 int m, i, j, t = 0, ans = 0;
11 char visited[MAXm] = { 0 };
12 str stack[MAXm];
13 int A[MAXm][MAXm], T[MAXm], C[MAXm];
14 scanf("%d%d", &stack[0].n, &m);
15 for (i = 1; i <= m; ++i)
16 scanf("%d", T + i);
17 for (i = 1; i <= m; ++i)
18 scanf("%d", C + i);
19 for (i = 0; i <= m; ++i)
20 for (j = 0; j <= m; ++j)
21 scanf("%d", &A[i][j]);
22 stack[0].x = 0;
23 stack[0].i = 0;
24 while (t >= 0) {
25 if (t == m) {
26 ++ans;
27 visited[stack[t].x] = 0;
28 --t;
29 } else {
30 while (++stack[t].i <= m)
31 if (A[stack[t].x][stack[t].i] + T[stack[t].i] < stack[t].n
32 && !visited[stack[t].i]) {
33 stack[t+1].i = 0;
34 stack[t+1].x = stack[t].i;
35 stack[t+1].n = stack[t].n - A[stack[t].x][stack[t].i]
36 - T[stack[t].i] + C[stack[t].i];
37 visited[stack[t++].i] = 1;
38 break;
39 }
40 if (stack[t].i > m)
41 visited[stack[t--].x] = 0;
42 }
43 }
44 printf("%d\n", ans);
45 return 0;
46 }
标答解释
第一次写题解,希望以后可以坚持写下去,写的不好,大家见谅~