动态规划的引入-挖地雷
继续昨天的练习,今天又做了题单里的一些题,选了一道比较有意思的题作为本文内容。
题目: 挖地雷
题目内容:
在一个地图上有NN个地窖(N \le 20)(N≤20),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。
输入格式:
有若干行。
第1行只有一个数字,表示地窖的个数N。
第2行有N个数,分别表示每个地窖中的地雷个数。
第3行至第N+1行表示地窖之间的连接情况:
第3行有n-1个数(00或11),表示第一个地窖至第2个、第3个、…、第n个地窖有否路径连接。如第3行为11000…0,则表示第1个地窖至第2个地窖有路径,至第3个地窖有路径,至第4个地窖、第5个、…、第n个地窖没有路径。
第4行有n-2个数,表示第二个地窖至第3个、第4个、…、第n个地窖有否路径连接。
… …
第n+1行有1个数,表示第n-1个地窖至第n个地窖有否路径连接。(为0表示没有路径,为1表示有路径)。
输出格式:
有两行,第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。第二行只有一个数,表示能挖到的最多地雷数。
输入输出样例:
输入
5
10 8 4 7 6
1 1 1 0
0 0 0
1 1
1
输出
1 3 4 5
27
解题过程:
每个地窖的属性比较多,所以用了一个结构体来存储,mineNum表示该地窖藏得地雷数目,mineMax表示通往当前地窖的地雷数和最大的那条路的地雷数(算上本身),pre是在地雷数最多的那条路该结点的前驱结点,link存储通往该地窖的所有前驱结点。
struct{
int mineNum;
int mineDp;
int mineMax;
int pre;
vector<int> link;
}cellar[100];
在通往当前地窖的地雷数和最大的那条路的地雷数 与 前一个地窖的dp值 做选择,这里顺便还存储了前驱结点,方便之后输出路径,代码如下:
void dp(){
for(int i=2;i <= cellarNum;i++){
cellar[i].mineMax = 0;
for(int j=0;j<cellar[i].link.size();j++){
//比较出哪条通往该地窖的路的地雷最多
if(cellar[i].mineMax < cellar[cellar[i].link[j]].mineMax){
cellar[i].mineMax = cellar[cellar[i].link[j]].mineMax;
cellar[i].pre = cellar[i].link[j]; //存储前驱结点
}
}
cellar[i].mineMax += cellar[i].mineNum;
cellar[i].mineDp = max(cellar[i].mineMax,cellar[i-1].mineDp);
}
}
代码写完了第一次交没过,仔细看了下题才发现还要输出路径,想了很久没想出来怎么在现基础上输出(不想重写代码),后来借鉴了一个题解,存储前驱结点,最后用递归的方式输出路径:
void showWay(int x){
if(cellar[x].pre == 0) {
cout << x << " " ;
return;
}
else showWay(cellar[x].pre);
cout << x << " ";
}
全部代码:
#include<bits/stdc++.h>
using namespace std;
//通往当前地窖的地雷数和最大的那条路的地雷数 与 前一个地窖的dp值 做选择
struct{
int mineNum;
int mineDp;
int mineMax;
int pre;
vector<int> link;
}cellar[100];
int cellarNum;
void init(){
cin >> cellarNum;
for(int i=1;i <= cellarNum;i++){
cin >> cellar[i].mineNum;
}
for(int i=1;i < cellarNum;i++){
for(int j=i+1;j <= cellarNum;j++){
int x;
cin >> x;
if(x==1) cellar[j].link.push_back(i);
}
}
cellar[1].mineDp = cellar[1].mineNum;
cellar[1].mineMax = cellar[1].mineNum;
cellar[1].pre = 0;
}
void showWay(int x){
if(cellar[x].pre == 0) {
cout << x << " " ;
return;
}
else showWay(cellar[x].pre);
cout << x << " ";
}
void dp(){
for(int i=2;i <= cellarNum;i++){
cellar[i].mineMax = 0;
for(int j=0;j<cellar[i].link.size();j++){
//比较出哪条通往该地窖的路的地雷最多
if(cellar[i].mineMax < cellar[cellar[i].link[j]].mineMax){
cellar[i].mineMax = cellar[cellar[i].link[j]].mineMax;
cellar[i].pre = cellar[i].link[j]; //存储前驱结点
}
}
cellar[i].mineMax += cellar[i].mineNum;
cellar[i].mineDp = max(cellar[i].mineMax,cellar[i-1].mineDp);
}
}
int main(){
init();
dp();
for(int i=1;i<=cellarNum;i++){
if(cellar[i].mineMax == cellar[cellarNum].mineDp){
showWay(i);
cout << endl;
break;
}
}
cout << cellar[cellarNum].mineDp;
}
心得:
在题解里发现的用递归来输出路径的方法很有意思,很多题目应该都可以用到。