个人理解:dp算法是一种理想状态的算法,假设在这一次之前所有的行动都是最优的,那么挑选出连接这一次状态的(也就是上一次行动)的最有情况加上这一次情况的奖励,那么就是当前这个状态下最优的。
总结了一下,主要是针对简单的模板题,这些题目都是我根据hdu的acm step中提炼出来的。
其实每一个dp算法,都需要有一个dp数组,无论是一维的还是二位的,用于保存到目前为止的最优状态。
可以总结一个公式,在dp题型里面,基本上都会有如下形式的公式出现
dp[i][j] = max(dp[i][j],dp[i-1][j]或者dp[i][j-1]或者都有)
另外需要注意的一点,就是初始值的给定。下面是一些题目:
FatMouse's Speed:
这个题目就是让求最长的符合条件的老鼠,并记下编号:
先上代码:
#include <iostream>
#include <iomanip>
#include<queue>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<iomanip>
#include<string.h>
#include<sstream>
#include<string>
//定义函数段
#define repf(i,a,b) for(int i =(a);i<(b);i++)
using namespace std;
struct mouse{
public :
int num ;
int weight;
int speed ;
};
bool cmp(mouse a ,mouse b )
{
if(a.weight==b.weight)
return a.speed>b.speed;
return a.weight<b.weight;
}
struct node{
public :
int num ;//记录之前有多少个符合要求的mouse
int pre;
} dp[1000];
mouse mice[1000];
int main() {
int index = 0 ;
while(cin>>mice[index].weight&&cin>>mice[index].speed)
{
mice[index].num=index+1;
index ++ ;
}
repf(i,0,1000)
{
dp[i].num = 1;//符合上述要求的并且包括自己的这个串上有多少个老鼠
dp[i].pre = 0;
}
sort(mice,mice+index,cmp);
int Max = 0 ,beginIndex;
repf(i,0,index)
{
repf(j,0,i)
{
//判断是不是辐射最上层的要求
if(mice[j].speed>mice[i].speed&&mice[j].weight<mice[i].weight)
{
//看看长度如果小的话,那么就可以加
if(dp[i].num<dp[j].num+1)
{
//更新最长节点数
dp[i].num=dp[j].num+1;
//更新上一个老鼠的位置
dp[i].pre=j;
}
}
}
//更新最大值
if(dp[i].num>Max)
{
Max=dp[i].num;
beginIndex = i ;
}
}
//这个其实完全没必要,直接一个循环,就是结果。
int m[1000];
repf(k,0,Max)
{
m[k] = beginIndex;
beginIndex = dp[beginIndex].pre;
}
//输出个数
cout << Max<<endl;
//输出路径
for(int k = Max-1;k>=0;k--)
{
cout << mice[m[k]].num<<endl;
}
return 0;
}
这个题目里边使用的dp是在这个位置:
//看看长度如果小的话,那么就可以加
if(dp[i].num<dp[j].num+1)
{
//更新最长节点数
dp[i].num=dp[j].num+1;
//更新上一个老鼠的位置
dp[i].pre=j;
} 虽然有点变形,因为还需要对dp[i].pre记录前驱,所以没法写成一个max得到结果。
if(dp[i].num<dp[j].num+1)
{
//更新最长节点数
dp[i].num=dp[j].num+1;
//更新上一个老鼠的位置
dp[i].pre=j;
} 虽然有点变形,因为还需要对dp[i].pre记录前驱,所以没法写成一个max得到结果。
而这个里边需要大家记住的是,每次的最佳状态都是基于上一次的,而这种没法明显的到上一次的题目(上一次哪个?最长,虽然都是最佳状态,我并不知道),就需要这种for i..... for j ; j<i ; 这种结构,我们来看第二个题目。
命运
上代码,这个题目就很亲民了,每次当前状态的dp都是上一次dp中最大的加上当前的奖励值
上代码:
#include <iostream>
#include <iomanip>
#include<queue>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<iomanip>
#include<string.h>
#include<sstream>
#include<string>
//定义函数段
#define repf(i,a,b) for(int i =(a);i<(b);i++)
using namespace std;
const int inf = -1000;
int data[21][1001];
int dp[21][1001];
int main() {
int num,r,c,Max ;
cin>>num;
while(num--){
cin >> r >> c ;
for(int i = 1 ; i <=r;i++)
{
for(int j = 1 ;j <= c ; j++)
{
cin>>data[i][j];
}
}
memset(dp,0,sizeof(dp));
for(int i = 1 ; i <= r ; i ++)
{
for(int j = 1 ; j <=c ; j ++)
{
if(i==1&&j==1)
{
dp[i][j]=data[i][j];
continue ;
}
Max = -1000;
for(int k = 1 ; k <j ;k++)
{
if(j%k==0)
{
Max=max(Max,dp[i][k]);
}
}
if(i-1>=1)
{
Max = max(Max,dp[i-1][j]);
}
if(j-1>=1)
{
Max = max(Max,dp[i][j-1]);
}
dp[i][j] = Max+data[i][j];
}
}
cout << dp[r][c] <<endl;
}
return 0;
}
下一个题目:
免费馅饼
这个题目还是两个日常for循环走起,题目给的前后信息很明确,代码如下:
#include <iostream>
#include <iomanip>
#include<queue>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<iomanip>
#include<string.h>
#include<sstream>
#include<string>
//¶¨Ò庯Êý¶Î
#define repf(i,a,b) for(int i =(a);i<(b);i++)
#define repfe(i,a,b) for(int i =(a);i<=(b);i++)
using namespace std;
const int inf = -1000;
int data[100001][12];
int dp[100001][12];
int main() {
int num,position,t,Max,MaxT = 0,tempDp = -1;
while(cin>>num&&num!=0)
{
memset(data,0,sizeof(data));
repfe(i,1,num)
{
cin>> position>> t ;
if(t>MaxT)
{
MaxT = t ;
}
data[t][position] ++;
}
memset(dp,0,sizeof(data));
dp[1][4] = data[1][4];
dp[1][5] = data[1][5];
dp[1][6] = data[1][6];
repfe(i,2,MaxT)
{
repf(j,0,11)
{
tempDp = -1 ;
tempDp = max(tempDp,dp[i-1][j]);
if(j<10)
tempDp = max(tempDp,dp[i-1][j+1]);
if(j>0)
tempDp = max(tempDp,dp[i-1][j-1]);
dp[i][j] = tempDp + data[i][j];
// cout << j <<" " << i <<" "<<dp[j][i]<<endl;
}
}
int res = 0 ;
repf(i,0,12)
{
if(dp[MaxT][i]>res)
{
res = dp[MaxT][i];
}
}
cout <<res <<endl;
}
return 0;
}
这个题目有两个坑,一个是你初始化的时候,要正确,不是全0就完事了,另一个是你需要搞清楚循环嵌套的顺序,是先时间,还是先位置。
下一个:
直接上地址:
点击打开链接
这个题目就是,没有明确前后信息,需要你自己找,也就是 j<i的那种循环。
本文深入讲解了DP算法的核心思想及应用技巧,通过多个实例详细分析了如何构建DP数组,确定状态转移方程,并给出了具体实现代码。
7万+

被折叠的 条评论
为什么被折叠?



