动态规划

      动态规划的实质是将较大问题问题分解为较小的同类子问题。与分治法和贪心法不同的是,动态规划法利用最优子结构,自底向上从子问题的最优解逐步构造出整个问题的最优解。

      设计一个动态规划算法,通常可以按一下4个步骤进行:

      (1)刻画最优解的结构特性;

      (2)递归定义最优解值;

      (3)自底向上计算最优解值;

      (4)根据计算得到的信息构造一个最优解。

      一个最优化多步决策问题是否适合用动态规划方法求解有两个要素:最优子结构核重叠子问题。

     虽然动态规划法也是基于分解思想的,但由于子问题往往是重叠的,为了避免重复计算,动态规划算法采用字自底向上的方式进行计算,并且保存已求解的子问题的最优解值。当这些子最优解值被重复引用时无需重新计算,因而节省大量计算时间。

     下面是我的题解,记录于此。

1.斐波拉契数列

http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1003

     先从斐波拉契数来窥探动态规划问题。既然斐波那契数就由之前的两数相加得到,那么循环中我们记录它的前两个数,自底向上求到斐波拉契数。

 1 #include<iostream>

 2 using namespace std;

 3 

 4 int main()

 5 {

 6     int n,i;

 7     cin>>n;

 8     if(n==0||n==1) 

 9         cout<<n<<endl;

10     else

11     {

12         int f1=0,f2=1,f3;

13         for(i=2;i<=n;i++)

14         {

15             f3=f1+f2;

16             f1=f2;

17             f2=f3;        

18         }

19         cout<<f3<<endl;

20     }

21     return 0;

22 }
View Code

2.最长递减子序列

http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1161

     当前每个数的最长子序列依赖于它的子序列,因此我们自后向前记录每个数的最长子序列,最后输出最大值即可。

 1 #include<iostream>

 2 using namespace std;

 3 

 4 int main()

 5 {

 6     int n,*a,*count,i,j,max;

 7     cin>>n;

 8     a=new int[n];

 9     count=new int[n];

10     for(i=0;i<n;i++)

11     {

12         cin>>a[i];

13         count[i]=1;

14     }

15     for(i=n-1;i>=0;i--)

16     {

17         max=0;

18         for(j=i+1;j<n;j++)

19             if(a[j]<a[i]&&max<count[j]) 

20                 max=count[j];

21         count[i]+=max;

22     }

23     for(i=0;i<n;i++)

24     {

25         if(max<count[i])

26             max=count[i];

27     }

28     cout<<max<<endl;

29     delete []a;

30     delete []count;

31     return 0;

32 }
View Code

 3.最少硬币问题

http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1221

      最开始考虑贪心法,WA。换DP,AC!看来还是没有深刻区分两类问题。其中,num数组记录要使用当前种类硬币的个数,count数组记录需要找的钱数的最少硬币个数。这里18块由13块跟硬币5组成,13块由8块跟硬币5组成,8块由3块跟硬币5组成,3块由1块跟硬币2组成,1块由硬币1得到。反过来也就是从1块钱开始,自底向上记录需要找的钱数的最少硬币个数。

 1 #include<iostream>

 2 using namespace std;

 3 

 4 int main()

 5 {

 6     int n,*T,*Coins,i,j,m,*count,*num;

 7     cin>>n;

 8     T=new int[n];

 9     Coins=new int[n];

10     for(i=0;i<n;i++)

11         cin>>T[i]>>Coins[i];

12     cin>>m;

13     count=new int[m+1];

14     num=new int[m+1];

15     for(i=1;i<=m;i++)

16         count[i]=0xfffffff;

17     count[0]=0;

18     for(i=0;i<n;i++)

19     {

20         for(j=0;j<=m;j++)

21             num[j]=0;

22         for(j=0;j<=m-T[i];j++)

23         {

24             if(num[j]<Coins[i]&&count[j]+1<count[j+T[i]])

25             {

26                 count[j+T[i]]=count[j]+1;

27                 num[j+T[i]]=num[j]+1;

28             }

29         }        

30     }

31     if(count[m]!=0xfffffff)

32         cout<<count[m]<<endl;

33     else

34         cout<<"-1"<<endl;

35     delete []T;

36     delete []Coins;

37     delete []count;

38     delete []num;

39     return 0;

40 }
View Code

4.数字三角形

http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1989

 1 #include<iostream>

 2 using namespace std;

 3 

 4 inline int max(int a,int b)

 5 {

 6     return a>b?a:b;

 7 }

 8 const int MAX=101;

 9 

10 int main()

11 {

12     int t,n,i,j;

13     int a[MAX][MAX];

14     int p[MAX][MAX];

15     cin>>t;

16     while(t--)

17     {

18         cin>>n;

19         for(i=0;i<n;i++)

20             for(j=0;j<=i;j++)

21                 cin>>a[i][j];        

22         for(i=0;i<n;i++)

23             p[n-1][i]=a[n-1][i];

24         for(i=n-2;i>=0;i--)

25         {

26             for(j=0;j<=i;j++)

27             {

28                 p[i][j]=a[i][j]+max(p[i+1][j],p[i+1][j+1]);

29             }

30         }

31         cout<<p[0][0]<<endl;

32     }

33     return 0;

34 }
View Code

5.吃苹果

http://acm.njupt.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=1060

      动态方程p[i][j]=max(p[i-1][j]+p[i-1][j-1])+(有苹果+1)。

 1 #include<iostream>

 2 using namespace std;

 3 #define MAXT 1001

 4 #define MAXK 31

 5 inline int max(int a,int b)

 6 {

 7     return a>b?a:b;

 8 }

 9 int main()

10 {

11     int t,k,i,j;

12     cin>>t>>k;

13     int a[MAXT];

14     int p[MAXT][MAXK];

15     for(i=1;i<=t;i++)

16         cin>>a[i];

17     p[0][0]=0;

18     for(i=1;i<=t;i++)

19     {

20         p[i][0]=p[i-1][0]+a[i]%2;

21         for(j=1;j<=k;j++)

22         {

23             p[i][j]=max(p[i-1][j-1],p[i-1][j])+(j%2==(a[i]-1)?1:0);    

24         }

25     }

26     int max=0;

27     for(j=1;j<=k;j++)

28         if(p[t][j]>max) max=p[t][j];

29     cout<<max<<endl;

30     return 0;

31 }
View Code

6.最长公共子序列

      动态方程c[i][j]=c[i-1][j-1]                      s1[i]=s2[j];

                 c[i][j]=max(c[i][j-1],c[i-1][j])    s1[i]≠s2[j];

 1 #include<iostream>

 2 #include<string>

 3 using namespace std;

 4 

 5 void LCS(const string s1,const string s2)

 6 {

 7     int i,j;

 8     int len1=s1.length();

 9     int len2=s2.length();

10     int **c=new int*[len1+2];

11     for(i=0;i<=len1;i++)

12         c[i]=new int[len2+2];

13     for(i=0;i<=len1;i++)

14         c[i][0]=0;

15     for(i=0;i<=len2;i++)

16         c[0][i]=0;

17     for(i=1;i<=len1;i++)

18     {

19         for(j=1;j<=len2;j++)

20         {

21             if(s1[i-1]==s2[j-1])

22                 c[i][j]=c[i-1][j-1]+1;

23             else if(c[i-1][j]>=c[i][j-1])

24                 c[i][j]=c[i-1][j];

25             else

26                 c[i][j]=c[i][j-1];

27         }

28     }

29     /******* c矩阵 *********

30     for(i=0;i<=len1;i++)

31     {

32         for(j=0;j<=len2;j++)

33             cout<<c[i][j]<<" ";

34         cout<<endl;

35     }

36     ************************/

37     cout<<c[len1][len2]<<endl;

38     for(i=0;i<=len1;i++)

39         delete []c[i];

40     delete []c;

41 }

42 

43 int main()

44 {

45     string s1,s2;

46     while(cin>>s1>>s2)

47     {

48         LCS(s1,s2);

49     }

50     return 0;

51 }
View Code

7.待续

...


      通过这几题的练习,应该可以解决一些简单的DP问题了。

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