动态规划

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

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

posted @ 2013-11-01 17:07  七年之后  阅读(283)  评论(0)    收藏  举报