天天写算法之回顾总结dp算法

个人理解: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得到结果。
而这个里边需要大家记住的是,每次的最佳状态都是基于上一次的,而这种没法明显的到上一次的题目(上一次哪个?最长,虽然都是最佳状态,我并不知道),就需要这种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的那种循环。

猜你喜欢

转载自blog.csdn.net/qq_36616268/article/details/80010179