动态规划的引入2

动态规划的引入-挖地雷

继续昨天的练习,今天又做了题单里的一些题,选了一道比较有意思的题作为本文内容。
题目: 挖地雷
题目内容:
在一个地图上有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;
} 

心得:
在题解里发现的用递归来输出路径的方法很有意思,很多题目应该都可以用到。

猜你喜欢

转载自blog.csdn.net/weixin_45218241/article/details/112248499