动态规划

从今天开始学习动态规划,个人觉得这是一个比较难的问题。我一直不敢做,但是为了这次能够参加省赛不得不去学了。看着以前的博文,觉得自己懂得知识好少,还有很多虽然写了,但是还是懵懵懂懂,算了,还是一步步来,以后再去深挖吧,最起码先要懂有这回事吧~~~加油!!

动态规划一般可分为线性DP(导弹拦截),区域DP(石子合并),树形DP(二分查找树),背包DP(背包问题,装箱问题)四类。

动态规划常用来求解决策过程中最优化的问题

但是这个算法相比别的方法而言,它有章可循,一般的步骤为:

(1)找出最优解的性质,并刻画其结构特征;

(2)递归地定义最优解(写出状态转移方程)。

(3)以自顶向下或自底向上的方式算出最优解。

最难的部分一般都是写出状态转移方程,可以说这个步骤如果出来了,这个程序大概也就出来了。

状态转移方程:用来表示前后阶段关系的方程。有点像高中数列中的求通项公式。

使用动态规划的算法:最长单调子序列,最长公共子序列,Floya-Warshall算法,Viterbi算法

理论东西不多说,可以自己找下其它资料,这里直接用题目进行分析:

袁老师是以这个题目为例题讲的NYOJ 18(数塔),题目的意思是:从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
可以采用自顶向下的算法或自底向上算法。

对于这道题一般都采用自底向上的算法,因为按照这种算法的话最后的第一层只有一个数,直接输出即可。

解法一:自底向上算法,倒推,每一步保存(i,j)位置与下面相邻的两个数(位置为(i+1,j)和(i+1,j+1))的最大值的和.一直到第一层即可。状态转移方程为:DP[i][j]+=max(DP[i+1][j+1],DP[i+1][j+1]),然后输出DP[0][0];

#include<iostream>
#include<cstring>
using namespace std;
const int MAX=110;
#define max(a,b) a>b?a:b
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int n,DP[MAX][MAX];
int main()
{   cin>>n;
    CLR(DP,0);
    for(int i=0;i<n;i++)
        for(int j=0;j<=i;j++)
            cin>>DP[i][j];
    for(int i=n-2;i>=0;i--)
        for(int j=0;j<=i;j++)
            DP[i][j]+=max(DP[i+1][j],DP[i+1][j+1]);           
    cout<<DP[0][0]<<endl;    
    return 0;
}

解法二:自顶向下算法,保存肩上的相邻两个数的最大值的和,状态转移方程为:DP[i][j]+=max(DP[i-1][j-1],DP[i-1][j])

#include<iostream>
#include<cstring>
using namespace std;
const int MAX=110;
#define max(a,b) a>b?a:b
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int n,maxt,DP[MAX][MAX];
int main()
{   cin>>n;
    CLR(DP,0);
    maxt=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
        {   cin>>DP[i][j];  
            DP[i][j]+=max(DP[i-1][j-1],DP[i-1][j]);
            maxt=max(maxt,DP[i][j]);
        }    
    cout<<maxt<<endl;    
    return 0;
}

例题2:NYOJ 17(最长单调递增子序列),设DP[i]为到i位置满足条件的最长递增子序列的长度,状态转移方程为:DP[i]=max(DP[i],DP[j]+1),其中(0<j<i)

#include<iostream>
#include<string>
#include<numeric>
#include<algorithm>
using namespace std;
const int MAX=10010;
string Str;
int num,DP[MAX];
int main()
{   cin>>num;
    cin.get();
    while(num--)
    {   cin>>Str;
        fill(DP,DP+MAX,1);
        for(int i=0;i<Str.length();i++)
            for(int j=0;j<i;j++)
                if(Str[i]>Str[j]) DP[i]=max(DP[i],DP[j]+1);
        cout<<*max_element(DP,DP+Str.length())<<endl;        
    }
    return 0;
}

和上面这个题目一样的是NYOJ 79(拦截导弹),状态转移方程为DP[i]=max(DP[i],DP[j]+1)(只不过这个时候是Arr[i]<Arr[j]).

例题2:POJ 1088(滑雪),这是袁老师讲的第二个题目,今天自己做下,类似迷宫,只能朝四个方向移动。和上面的例题例题差不多,只是这个是求最长递减子序列的长度,且由一维的换为二维的,需要考虑四个方向。不多解释,应该容易看懂~

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAX=110;
#define max(a,b) a>b?a:b
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int dx[4]={0,1,0,-1},dy[4]={-1,0,1,0};
int num,row,col,map[MAX][MAX],DP[MAX][MAX];
bool Inside(int x,int y)
{   return x>=0&&x<row&&y>=0&&y<col; 
}
int DFS(int Sx,int Sy)
{   if(DP[Sx][Sy]) return DP[Sx][Sy]; 
    for(int i=0;i<4;i++)
    {   int Newx=Sx+dx[i];
        int Newy=Sy+dy[i];
        if(map[Newx][Newy]<map[Sx][Sy]&&Inside(Newx,Newy))
            DP[Sx][Sy]=max(DP[Sx][Sy],DFS(Newx,Newy));         
    }
    return DP[Sx][Sy]++;
}        
int main()
{   scanf("%d",&num);
    while(num--)
    {   CLR(DP,0);
        scanf("%d%d",&row,&col);
        for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
                scanf("%d",&map[i][j]);
        int maxt=0;
        for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
                maxt=max(maxt,DFS(i,j)); 
        printf("%d\n",maxt);           
    }
    return 0;
}

例题3:NYOJ 30(Gone fishing)英语题目,今天开始翻译英语题目,我不懂语法,单词靠有道!!!呀~这个题目好长哦~~,不愿意翻译了,我翻译一句都要好久,而且还是乱翻译的,还是硬着头皮吧,只要翻译出来能看懂大概的意思就行.....
 


 


 

 

你可能感兴趣的:(动态规划)