zoukankan      html  css  js  c++  java
  • 动态规划

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

          设计一个动态规划算法,通常可以按一下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问题了。

  • 相关阅读:
    on条件与where条件的区别
    Sqlserver中如何创建链接服务器
    SQL Server 2008 R2 主从数据库同步
    网站性能优化
    检索COM 类工厂中CLSID 为 {00024500-0000-0000-C000-000000000046}的组件时失败
    text输入框中按下enter键时阻止刷新页面
    关于以DataTable形式批量写入数据的案例
    关于Excel导入的HDR=YES; IMEX=1详解
    一条结合where、group、orderby的linq语法
    Linq使用Group By经验总结
  • 原文地址:https://www.cnblogs.com/Rosanna/p/3401251.html
Copyright © 2011-2022 走看看