【洛谷】P2196挖地雷

杂谈:

递推和递归的一个比较重要的区别是,递推需要先求出所有前提条件,比如

dp[i] = max(dp[i], dp[j] + num[i]);

需要先求出dp[ j ],这个时候就需要规划一下遍历的顺序,让dp[ j ]先被求出。

而递归过程中,若

m = max(m, dp(j) + num[i]);

则只需要在递归函数的适当位置做好return即可。

 

 dp[i]表示以第i号地洞为起点,这样做的好处是方便后面输出路径,不过这样就需要从后往前遍历地洞,如此才能保证递推公式里用到的前提条件提前被求出来过。

以这道题为例,递推公式为

前提条件指的是dp[ j ],也就是以第 j 号地洞为起点,能得到的最大地雷数。

#include<iostream>
using namespace std;

int N;
int num[25];        // num[i]记录第i号地洞的地雷数
bool can[25][25];   // can[i][j]表示第i号地洞能否到达第j号地洞
int dp[25];         // dp[i]表示以第i号地洞为起点的地雷最大值 
int nextDot[25];    // nextDot[i]表示第i号地洞的下一个地洞是哪个
                    // 用于后面输出路径

int main()
{
	scanf("%d", &N);
	for(int i=1;i<=N;++i)
	{
		scanf("%d", &num[i]);
		dp[i] = num[i]; // 初始化dp[i]为i号地洞里的地雷数 
	}
	for(int i=1;i<=N;++i)
		for(int j=i+1;j<=N;++j)
			cin>>can[i][j];
			
	int m = 0;
	int start = 0;         // 标记从哪个地洞开始走,可以得到最多地雷
	for(int i=N;i>0;--i)   // 从后往前遍历地洞,保证递推公式里的前提条件被求出来过
	{
		for(int j=i+1;j<=N;++j) 
// 因为是从一个地洞不断往下走,因此下一个地洞的编号需要从大于i开始
		{
			if(can[i][j] && dp[j] + num[i] > dp[i])
			{
				dp[i] = dp[j] + num[i];
				nextDot[i] = j; // 表示 i号地洞的下一个洞是 j 
			}
		}
		if(dp[i] > m)
		{
			m = dp[i];
			start = i; // 更新起始点 
		}
	}
	
	while(start) // 输出路径
	{
		cout<<start<<' ';
		start = nextDot[start];
	}
	cout<<endl<<m;
	
	return 0;
}

 下面是dp[ i ]表示以 i 号地洞为终点时能得到的最大地雷数的做法,需要格外注意的是这种写法下,输出路径需要反向输出,这里采用了递归输出的方法,这种方法可以掌握一下;另外就是两种写法遍历地洞的顺序有所不同

#include<iostream>
#include<string.h>
using namespace std;

int f[25]; // f[i] 表示以i号地洞为终点时能得到的最大地雷数 
int can[25][25];
int num[25];
int pre[25];
int N;

void dfs(int x)
{
	if(pre[x]) dfs(pre[x]);
	cout<<x<<' ';
}

int main()
{
	scanf("%d", &N);
	for(int i=1;i<=N;++i) scanf("%d", &num[i]);
	for(int i=1;i<N;++i)
		for(int j=i+1;j<=N;++j)
			scanf("%d", &can[i][j]);
	
	memset(f, -1, sizeof(f));
	int m = 0;
	int pos = 0;
	
	for(int k=1;k<=N;++k)
	{
		if(f[k] == -1)
			f[k] = num[k];
	
		for(int i=k-1;i>0;--i)
		{
			if(can[i][k] && f[i] + num[k] > f[k])
			{
				f[k] = f[i] + num[k];
				pre[k] = i; // 记录k号地洞的上一个洞是i 
			}
		}
		if(f[k] > m)
		{
			m = f[k];
			pos = k;
		}
	}
	
	dfs(pos);
	cout<<endl<<m;
	
	return 0;
}

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值