洛谷P2196 dp+前趋

本题AC代码如下:

#include<bits/stdc++.h>
using namespace std;
int s[100],flag[100],dp[100];
bool f[100][100];
void dfs(int t)
{
	if(flag[t])dfs(flag[t]);
	cout<<t<<' ';
}
int main()
{
	int n,sum=0,idx;
	cin>>n;
	for(int i=1;i<=n;i++)
	{cin>>s[i];dp[i]=s[i];}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
		cin>>f[i][j];
	dp[1]=s[1];
	sum=dp[1];
	for(int i=2;i<=n;i++)
	{
		for(int j=i-1;j>=1;j--)
		{
			if(f[j][i]&&dp[i]<dp[j]+s[i])
			{
				dp[i]=dp[j]+s[i];
				flag[i]=j;
			}
		}
		if(sum<dp[i])
		{
			sum=dp[i];
			idx=i;
		}
	}
	dfs(idx);
	cout<<endl<<sum<<endl;
	return 0;
}

众所周知,dp的关键在于找到相应的状态转移方程。那么此题的状态转移比较常规,就是找到前n个地雷坑中能收获的最大地雷数。
于是相应的,我们要用到记忆优化,即记录前n个能得到的最大地雷数为dp[n]。
于是生成代码:

if(dp[i]<dp[j]+s[i])dp[i]=dp[j]+s[i];

翻译成中文就是:如果上一个状态的前n个坑里选折的地雷最大值小于当前状态的前n-1个坑里选地雷的最大值加上当前坑的地雷数量,就跟新数据。


此题有一个比较妙的点就是前趋。
我们要输出路径,如果用逐一记录的方式,会容易出错,而如果用前趋的思想,可以使数据保持动态更行,最后直接输出即可。
现在聊一聊这个的过程实现:
一共有n个坑,如果要得出前 t 个坑中能得出的最大值,就一定存在一条路,而这条路如果单纯用数组一个一个表示,其动态更新与最优解不得已保证,但是如果我们把这个路单一表示就可以简单的多。
比如:当前坑号为4号,我们可以从2号和3号和5号处到达4号。那么我们只用遍历三个坑,看哪个坑到4号的可以使地雷数达到最大,那么我就将4号指向某号,用于记忆。
使用前趋还需要记录最终状态,即到哪结束,这样才可以通过这个终态去反推全过程。


ps 本题我还出了一个致命错误,即没有初始化dp数组,要注意每个坑能达到的最大值最小一定是这个坑原有的地雷数量。所以生成代码:

for(int i=1;i<=n;i++)
	{cin>>s[i];dp[i]=s[i];}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值