一本通 1262:【例9.6】挖地雷 动态规划

1262:【例9.6】挖地雷
题目链接
题目:
【题目描述】
在一个地图上有n个地窖(n≤200),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径,并规定路径都是单向的,且保证都是小序号地窖指向大序号地窖,也不存在可以从一个地窖出发经过若干地窖后又回到原来地窖的路径。某人可以从任意一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使他能挖到最多的地雷。

【输入】
第一行:地窖的个数;

第二行:为依次每个地窖地雷的个数;

下面若干行:

xiyi //表示从xi可到yi,xi<yi。

最后一行为"00"表示结束。

【输出】
k1−k2−…−kv //挖地雷的顺序

挖到最多的雷。

【输入样例】

6
5 10 20 5 4 5
1 2
1 4
2 4
3 4
4 5
4 6
5 6
0 0

【输出样例】

3-4-5-6
34

思路:

要求:挖到的地雷值最大-最值问题 ,路径必须保证是通路 ,可以从任一一处开始挖 (重叠子问题、无后效性都满足,所以用动态规划)

  • 首先确定一下状态

    • 从前往后找地雷还是从后往前找-都需要判断路径,所以设置一个邻接矩阵rl[n] [n], 需要dp[n]存储包括挖到当前地雷时所有的(挖到之前/之后(从前往后挖)的)地雷最大值
    • 要输出顺序,所以需要得到一个序列b[n],要得到b[n]还需要知道挖到某个地窖时,前/后一个(从前往后挖)地窖得到位置,即一个关系数组pre[n]/post[n]。
    • 要清晰拿到b[n],还需要一个maxk来存放最开始的地雷位置/最后一个地雷位置(从前往后挖,回溯)
  • 确定选择/方程:

    • 挖到某个地窖,就选择所在前面/后面有通路的紧挨着的地窖最值+目前的地窖中地雷数目 即 max(dp[rl[i] [j]]+v[i],dp[i])
  • 边界条件:

    • 当没有通路时dp[i]值就是当前地窖数
    • 求最值时,maxn!!!一定要初始化 ,最多为0

参考代码1

#include <bits/stdc++.h>
#define MAXN  205
using namespace std;
int a[MAXN][MAXN],dp[MAXN],pre[MAXN] ;//均初始化为0 
int main(){
	int n ,maxn=0,maxk,v[MAXN],b[MAXN]; //maxn,maxk分别储存最值的值和最后一个地雷的位子。v,b分别储存地雷数和最终挖地雷的序列 
	vector<int> rl[MAXN];//动态数组存邻接矩阵-地雷连通关系图 
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i];
		dp[i] = v[i]; //直接dp初始化 
	}
	int x,y;
	while(cin>>x>>y) { //关系储存到邻接表rl 
		if(x==0&&y==0) break;
		rl[y].push_back(x);
	}
	for(int i=1;i<=n;i++){ //遍历地雷
		for(int j=0;j<rl[i].size();j++){
			if(dp[rl[i][j]]+v[i]>dp[i]) { //赋值并记录序列关系 
				dp[i] = dp[rl[i][j]]+v[i];
				pre[i] = rl[i][j];
			}
		}
		if(maxn<dp[i]){ //更新最值 
			maxn = dp[i];
			maxk = i;//记录当前最值的最后一个下地雷位子-下标 
		} 
	} 
	//回溯处理序列
	int k = 0;
	while(maxk>0){
		b[++k] = maxk;
		maxk = pre[maxk];
	}
//	cout<<k;
	for(int i=k;i>0;i--){
		cout<<b[i];
		if(i>1) cout<<"-";
	}
	cout<<endl;
	cout<<maxn;
	return 0;
} 

参考代码2

#include <bits/stdc++.h>
#define MAXN  205
using namespace std;
int a[MAXN][MAXN],dp[MAXN],post[MAXN] ;//均初始化为0 
vector<int> rl[MAXN];//动态数组存邻接矩阵-地雷连通关系图 
int main(){
	int n ,maxn=0,maxk,v[MAXN],b[MAXN]; //maxn,maxk分别储存最值的值和最后一个地雷的位子。v,b分别储存地雷数和最终挖地雷的序列 

	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>v[i];
		dp[i] = v[i]; //直接dp初始化 
	}
	int x,y;
	while(cin>>x>>y) { //关系储存到邻接表rl 
		if(x==0&&y==0) break;
		rl[x].push_back(y);
	}
	for(int i=n;i>0;i--){ //遍历地雷
		for(int j=0;j<rl[i].size();j++){
			if(dp[rl[i][j]]+v[i]>dp[i]) { //赋值并记录序列关系 
				dp[i] = dp[rl[i][j]]+v[i];
				post[i] = rl[i][j];
			}
		}
		if(maxn<dp[i]){ //更新最值 
			maxn = dp[i];
			maxk = i;//记录当前最值的开始的第一个下地雷位子-下标 
		} 
	} 
	//处理序列 
	int k = 0;
	while(maxk>0){
		b[++k] = maxk;
		maxk = post[maxk];
	}
	for(int i=1;i<=k;i++){
		cout<<b[i];
		if(i<k) cout<<"-";
	}
	cout<<endl;
	cout<<maxn;
	return 0;
} 
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值