本周学习了动态规划,第一次听说动态规划,还是在寒假的时候,那时候我初步接触算法,我的一个同学有一天晚上突然问我,你学到DP了吗?为了不丢人,我连忙从床上爬起来去查啥是DP,才知道这叫动态规划,只是没想到我现在才学这个,我比别人拉下的太多了。。。
目录
动态规划思想解析:
先把我从百度上扒下来的定义说一遍(主要是怕解释错了误导别人)
————通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
通俗的讲就叫:大事化小,小事化了;若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。(你也可以用打游戏来理解,你要打大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;
}
总结:
动态规划是我感觉最神奇的解法,它不同于暴力搜索,也不同于一般的贪心,能够以出乎人意料的时间复杂度解决一些问题。不过,动态规划的思维性比较强,必须会设好状态,正确写出状态转移方程,并且能够准确判断有无最优子结构。