北大暑期学习之动态规划

来源:互联网 发布:服务器与数据库的区别 编辑:程序博客网 时间:2024/05/20 16:00

例题一:数字三角形

7

3 8

8 1 0

2 7 4 4

4 5 2 6 5

此题是从顶部走到底部,所经过的路径上的数字之和最大,路径上的每一步只能左下或者右下走,求出最大和即可。

一.二维数组做

#include<iostream>

#include<algorithm>

#define max 101

Int D[max][max];

Int n;

Int maxsum(int i,int j)

{

If(i==n)

Return D[i][j];

Int x=maxsum(i+1,j);

Int y=maxsum(i+1,j+1);

Return max(x,y)+D[i][j];

}

Int main()

{

Int i,j;

Cin>>n;

For(i=1;i<=n;i++)

For(j=1;j<=n;j++)

Cin>>D[i][j];

Cout<<maxsum(1,1)<<endl;

}

此方法会超时

改进

如果每算出一个maxsum(r,j)就保存起来,下次用到其值时直接取用,则可免去重复计算。

#include<iostream>

#include<algorithm>

#define max 101

Int D[max][max];

Int n;

Int maxsum(int i,int j)

{

If(maxsum(i,j)!=-1)

Return maxsum[i][j];

If(i==n)

Maxsum[i][j]=D[i][j];

Else

{

Int x=maxsum(i+1,j);

Int y=maxsum(i+1,j+1);

Maxsum[i][j]=max(x,y)+D[i][j];

}

Return maxsum[i][j];

}

Int main()

{

Int i,j;

Cin>>n;

For(i=1;i<=n;i++)

For(j=1;j<=n;j++)

{

Cin>>D[i][j];

Maxsum[i][j]=-1;

}

Cout<<maxsum(1,1)<<endl;

}

递归成递推

#include<iostream>

#include<algorithm>

#define max 101

Int D[max][max];

Int n;

Int maxsum[max][max];

Int main()

{

Int i,j;

Cin>>n;

For(i=1;i<=n;i++)

For(j=1;j<=i;j++)

Cin>>D[i][j];

For(i=1;i<=n;i++)

Maxsum[n][j]=D[n][j];

For(i=n-1;i>=1;i--)

For(int j=1;j<=i;j++)

Maxsum[i][j]=max(maxsum[i+1][j],maxsum[i+1][j+1])+D[i][j];

Cout<<maxsum(1,1)<<endl;

}

空间优化

用一维数组

#include<iostream>

#include<algorithm>

#define max 101

Int D[max][max];

Int n;

Int *maxsum;

Int main()

{

Int i,j;

Cin>>n;

For(i=1;i<=n;i++)

For(j=1;j<=i;j++)

Cin>>D[i][j];

Maxsum=D[n];

For(int i=n-1;i>=1;--i)

For(int j=1;j<=i;j++)

Maxsum[j]=max(maxsum[j],maxsum[j+1])+D[i][j];

Cout<<maxsum[1]<<endl;

}

递归到动规的一般转化方法

递归有n个参数,就定义了一个n维数组,数组的下标是递归函数的参数的取值范围,数组元素的值是递归函数的返回值,这样就可以从边界值开始,逐步填充数组,相当于计算递归函数值的逆过程。

能用动规解题的问题的特点:

1.问题具有最优子结构性质。

2.无后效性

例题二:最长上升子序列

找出状态转移方程

人人为我递推型动态规划

#include<iostream>

#include<algorithm>

#define max 1010

Int a[max];

Int maxlen[maxn];

Int main()

{

Int n;

Cin>>n;

For(int i=1;i<=n;++i)

{

Cin>>a[i];

Maxlen[i]=1;

}

For(int i=2;i<=n;i++)

 

For(int j=1;j<i;j++)

{

If(a[i]>a[j])

Maxlen[i]=max(maxlen[i],maxlen[j]+1);

}

}

int ans=0;

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

   {

       ans=max(ans,maxlen[i]);

   }

   printf("%d\n",ans);

   return 0;

}

我为人人递推型动规程序

#include<iostream>

#include<algorithm>

#define max 1010

Int a[max];

Int maxlen[maxn];

Int main()

{

Int n;

Cin>>n;

For(int i=1;i<=n;++i)

{

Cin>>a[i];

Maxlen[i]=1;

}

For(int i=1;i<=n;i++)

 

For(int j=i+1;j<=n;j++)

{

If(a[j]>a[i])

Maxlen[j]=max(maxlen[j],maxlen[i]+1);

}

}

int ans=0;

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

   {

       ans=max(ans,maxlen[j]);

   }

   printf("%d\n",ans);

   return 0;

}

例题三:最长公共子序列

#include<iostream>

#include<cstring>

Using namespace std;

Char sz1[100];

Char sz2[1000];

Int maxlen[1000][1000];

Int main()

{

While(cin>>sz1>>sz2

{

Int length1=strlen(sz1);

Int length2=strlen(sz2);

Int ntemp;

Int i,j;

For(i=0;i<=length1;i++)

Maxlen[i][0]=0;

For(j=0;j<=length2;j++)

Maxlen[0][j]=0;

For(i=1;i<=length1;i++)

{

For(j=1;j<=length2;j++)

{

If(sz1[i-1]==sz2[j-1])

Maxlen[i][j]=maxlen[i-1][j-1]+1;

Else

Maxlen[i][j]=max(maxlen[i][j-1],maxlen[i-1][j]);

}

}

Cout<<maxlen[length1][length2]<<endl;

}

RETURN 0;

}

例题四:最佳加法表达式

#include<cstdio>

#include<cstring>

#include<cmath>

#include<algorithm>

using namespace std;

int dp[100][200];

char str[500];

int change(int x,int y)

{

   int t=0;

   for(int i=x;i<=y;i++)

   {

   t*=10;

   t+=(str[i]-'0');

   }

   return t;

}

int main()

{

   int n,m,i,j,k;

   while(scanf("%d%d",&n,&m)!=EOF)

   {

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

      {

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

        {

        dp[i][j]=999999999;

        }

      }

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

      {

         dp[0][i]=change(1,i);

      }

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

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

      for(k=i;k<=j;k++)

      {

         dp[i][j]=min(dp[i][j],dp[i-1][k]+change(k+1,j));

      }

      printf("%d\n",dp[m][n]);

   }

   return 0;

}

 

例题五:神奇的口袋

递归解法

#include<iostream>

Using namespace std;

Int a[30];

Int n;

Int ways(int w,int k)

{

If(w==0)

Return 1;

If(k<=0)

Rerturn 0;

Return ways(w,k-1)+ways(w-a[k],k-1);

}

Int main()

{

Cin>>n;

For(int i=1;i<=n;i++)

Cin>>a[i];

Cout<<ways(40,n);

Return0;

}

动规解法

#include<iostream>

Using namespace std;

Int a[30] ;int n;

Int ways[40][30];

Int main()
{
cin>>n;

Memset(ways,0,sizeof(ways));

For(int i=1;i<=n;i++)

{

Cin>>a[i];

Ways[0][i]=1;

}

ways[0][0]=1;

For(int w=1;w<=40;w++)

{

For(int k=1;k<=n;k++)

{

Ways[w][k]=ways[w][k-1];

If(w-a[k]>=0)

Ways[w][k]+=ways[w-a[k]][k-1];

}

}

Cout<<ways[40][n];

Return 0;

}

例题六:0-1背包问题

F[i][j]表示取前i种物品,使他们总体积不超过j的最优取法取得的价值总和

F[i][j]=max(F[i-1][j],F[i-1][j-w[i]]+d[i]);

f[n][m]=f[n-1][m-w[n]]+v[n]n件物品放入背包

f[n][m]=f[n-1][m] n件物品不放入背包里

#include<iostream>
using namespace std;

Int n=3403;

Int m=12881;

Int f[m];

Int c[n],v[n];

Int main()

{

  Int n,m;

  While(cin>>n>>m)

   {

     Int i,j;

     For(i=1;i<=n;i++)

     {

        Cin>>c[i]>>v[i];

     }

     Memset(f,0,sizeof(f));

     For(i=1;i<=n;i++)

     {

      For(j=m;j>=c[i];j--)

      {

       If(f[j]<f[j-c[i]]+v[i])

        f[j]=f[j-c[i]]+v[i];

      }

     }

    Cout<<f[m]<<endl;

   }

  Return 0;

}

排列计数

1 2 3 4的全排列 共有4!种,求第10个的排列数是?

先试1,1后有三个数3=6  6<10说明首位放1偏小

问题转化为求2开头的序列10-6=4 3=6  6>4说明首位为2

此思想还未掌握

状态压缩动态规划

 

0 0