动态规划学习心得

本周学习了动态规划,第一次听说动态规划,还是在寒假的时候,那时候我初步接触算法,我的一个同学有一天晚上突然问我,你学到DP了吗?为了不丢人,我连忙从床上爬起来去查啥是DP,才知道这叫动态规划,只是没想到我现在才学这个,我比别人拉下的太多了。。。

目录

动态规划思想解析:

动态规划和贪心的区别:

例题:

<1>最小正字段和

<2>回文字符串

总结:


动态规划思想解析:

先把我从百度上扒下来的定义说一遍(主要是怕解释错了误导别人)

————通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

通俗的讲就叫:大事化小,小事化了;若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。(你也可以用打游戏来理解,你要打大BOSS,就要从打小怪开始,解决完大魔王的部下,你才可以打败大魔王),或者说,你有一本高数作业,你一点没动,可是马上就要交了,怎么办,你需要召唤自己的好兄弟,让他们帮你写点(理想状态,请勿当真),每人都写一点。那自然就可以写完了。

动态规划和贪心的区别:

我在这一部分的学习的时候,发现其实动态规划和贪心还蛮像的,但是查过资料之后发现,两者其实有着不小的差别:动态规划问题中每一个状态一定是由上一个状态推导出来的,在这一点上区别于贪心算法,贪心算法是从局部直接选取最优解,并不依赖于之前计算出来的状态。

例题:

<1>最小正字段和

N个整数组成的序列a[1],a[2],a[3],…,a[n],从中选出一个子序列(a[i],a[i+1],…a[j]),使这个子序列的和>0,并且这个和是所有和>0的子序列中最小的。
例如:4,-1,5,-2,-1,2,6,-2。-1,5,-2,-1,序列和为1,是最小的。

思路:我们发现任意两个前缀和的差都代表了一个子段的和,只要两个前缀和之差比较小,那么他就可能是答案,所有我们要尽可能的找到这样的前缀和, 我们发现只要将前缀和进行排序,相邻两项的差就是最小的。但也要保证组成的了正序的序列。

#include<bits/stdc++.h>

using namespace std;
#define ll long long
const int N = 5e4 + 10;
int n, a[N];
ll sum[N];
struct node
{
	int id;
	ll x;
	bool operator <(const node b) const
	{
		if(x == b.x)
			return id < b.id;
		return x < b.x;
	}
}v[N];
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++) scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i], v[i].id = i, v[i].x = sum[i];
	sort(v, v + n + 1);
	ll ans = 1<<30;
	for(int i = 1; i < n; i++)
	{
		if(v[i].id > v[i - 1].id && v[i].x != v[i - 1].x)
			ans = min(v[i].x - v[i - 1].x, ans);
	}
	cout << ans << endl;
}

<2>回文字符串

所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba"。当然,我们给你的问题不会再简单到判断一个字符串是不是回文字符串。现在要求你,给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。

思路:本题可以这样想,如果这个字符串里面已经存在了回文串,那么剩下左边和右边的那些就是不对称的,所以在左边补上右边的字符,右边补上左边的字符,这样就会完整了,所以要补齐的字符数目就是总长度减去串内部的回文串长度,而内部的回文串长度则可以由本串和逆串来求出最长回文子序列,这就是基本的dp问题了

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

string fun(string str){
	string ans;
	int len=str.length();
	for(int i=len-1;i>=0;i--){
		ans.push_back(str[i]);
	}
	return ans;
} 


int main(){
	int n;
	cin >>n;
	for(int i=0;i<n;i++){
		string str1;
		cin >> str1;
		string str2=fun(str1);//将str1逆置 
		int len=str1.length();
		
		int dp[1500][1500];
		for(int i=0;i<=len;i++){
			dp[i][0]=dp[0][i]=0;
		}
		
		for(int i=1;i<=len;i++){
			for(int j=1;j<=len;j++){
				if(str1[i-1]==str2[j-1]){
					dp[i][j]=dp[i-1][j-1]+1;
				}else{
					dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
				}
			}
		}
		
		cout<<len-dp[len][len]<<endl;
	}
	return 0;
}

总结:

动态规划是我感觉最神奇的解法,它不同于暴力搜索,也不同于一般的贪心,能够以出乎人意料的时间复杂度解决一些问题。不过,动态规划的思维性比较强,必须会设好状态,正确写出状态转移方程,并且能够准确判断有无最优子结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值