个人理解: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的那种循环。