洛谷[2196]挖地雷

@挖地雷

题目大意

  有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;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值