题目大意
有n(n <= 20)个放有地雷的地窖,题目给出了每个地窖里地雷的数量,并用0,1表示了地窖之间是否有路连接。你可以从任意一个地窖开始(从前往后)挖地雷,求一条能挖到最多地雷的路以及能挖到地雷的最大数量。
输入输出
输入有若干行
第一行为地窖个数n
第二行为n个表示各地窖中地雷个数的数
(接下来的几行用1,0表示路径的有、无)
第三行有n-1个数,表示第1个地窖至第2、3……n个地窖是否有路
第四行有n-2个数,表示第2个地窖至第3、4……n个地窖是否有路
第n+1行有1个数,表示第n-1个地窖至第n个地窖是否有路
输出有两行
第一行为用空格隔开的最佳挖雷路径的地窖顺序编号
第二行为能挖到地雷的最大个数
样例输入:
5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1
样例输出:
1 3 4 5
27
算法讨论
这题主要是用动态规划来实现,状态转移方程如下:
f[i] = max{f[j]+bomb[i]}
j < i
road[i][j] == 1
也就是说,到第 i 个地窖最多能挖到的地雷应该是从第一个地窖到第 i-1 个地窖能挖到的最大地雷数加上 i 地窖的地雷地雷个数。当然,i、j之间得有路相连。而为了记录最佳路径,我们需要将每个地窖的最优”上接点”记录下来,也就是 j 的编号。因此,我们可以开一个数组给每个地窖存入两条数据:
· 到目前为止能挖到的最大地雷数
· 最优的的”上接点”的编号
当然,我们需要一个变量来储存到目前为止挖到最大地雷的地窖编号。当程序的主要部分运行完后,我们便可以将这个编号推进一个栈中,接着再将它的”上接点”推进去,再将”上接点”的”上接点”推进去,然后按顺序将栈顶输出、推出。
代码实现
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <stack>
using namespace std;
int main(){
stack<int> way; //定义一个栈来存放反的路径然后逐个按顺序输出
int n, cellar[20][2], bomb[20], road[20][20] = {0};
int result = 0, now = 0;
//result记录从开始到现在挖最多地雷的点的编号 now记录当前点最佳策略“上接点”
scanf("%d", &n);
for(int i = 0; i < n; i++){
scanf("%d", &bomb[i]);
cellar[i][0] = bomb[i]; //初始化原有炸弹数
cellar[0][1] = -1; //初始化“上接点”的编号,-1则表示起点
}
for(int i = 0; i < n-1; i++){
for(int j = i+1; j < n; j++){
scanf("%d", &road[i][j]); //记录i到j是否有路
//因为只能从前往后挖,所以不用记录j到i是否有路
}
}
for(int i = 1; i < n; i++){
int maxn = 0;//记录到i点为止能拿到的最多地雷
now = -1;
for (int j = 0; j < i; j++) {
if (road[j][i] && cellar[j][0] > maxn) {
maxn = cellar[j][0];
now = j;
}
}
cellar[i][0] += maxn;
cellar[i][1] = now;
result = cellar[result][0] > cellar[i][0] ? result : i;
}
now = result;
way.push(result); //将终点推入栈
while (cellar[now][1] != -1) {
way.push(cellar[now][1]);
now = cellar[now][1];
} //通过每个节点的“上接点”编号将终点到起点全部推进栈
while (!way.empty()) {
printf("%d ", way.top()+1);
way.pop();
}
printf("\n%d\n", cellar[result][0]);
return 0;
}