复习/学习DP的长记

来源:互联网 发布:淘宝新店怎样引流 编辑:程序博客网 时间:2024/06/11 14:21

01背包问题
采药
二维

#include<cstdio>#include<iostream> #include<cstring>using namespace std;int t,m;const int maxn=1005;int v[maxn],tm[maxn],dp[maxn][maxn];int main(){    memset(v,0,sizeof(v));    memset(tm,0,sizeof(tm));    memset(dp,0,sizeof(dp));    scanf("%d%d",&t,&m);    for(int i=1;i<=m;i++)    {        scanf("%d%d",&tm[i],&v[i]);    }    for(int i=1;i<=m;i++)    for(int j=0;j<=t;j++)    {        if(j-tm[i]>=0) dp[i][j]=max(dp[i-1][j-tm[i]]+v[i],dp[i-1][j]);        else dp[i][j]=dp[i-1][j];    }    printf("%d\n",dp[m][t]);    return 0; }//若题目要求:恰好装满容积为V的背包的最大价值:初始化dp[0][i](i!=0)=-INF//保证所有的状态都是从起点dp[0][0]转移得到的 每次装物品时,必定从已经装了体积为j-ci的物品的背包转移过来。 

一维

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=1005;int t,m;int v[maxn],tm[maxn],dp[maxn];int main(){    scanf("%d%d",&t,&m);    for(int i=1;i<=m;i++) scanf("%d%d",&tm[i],&v[i]);    for(int i=1;i<=m;i++)    for(int j=t;j>=tm[i];j--)    {        dp[j]=max(dp[j],dp[j-tm[i]]+v[i]);    }    printf("%d\n",dp[t]);    return 0;}

金明的预算方案

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;int N,m,num;int sfv[300][3],sfw[300][3],dp[40000+5];int main(){    scanf("%d%d",&N,&m);    for(int i=1;i<=m;i++)    {        int v,w;        scanf("%d%d%d",&v,&w,&num);        if(!num)        {            sfv[i][0]=v;            sfw[i][0]=w;        }        else         {            if(!sfv[num][1])            {                sfv[num][1]=v;                sfw[num][1]=w;                                  }            else             {                sfv[num][2]=v;                sfw[num][2]=w;            }        }    }    for(int i=1;i<=m;i++)    for(int j=N;j>=sfv[i][0];j--)    {        dp[j]=max(dp[j-sfv[i][0]]+sfv[i][0]*sfw[i][0],dp[j]);        if(j>=sfv[i][0]+sfv[i][1])        dp[j]=max(dp[j-sfv[i][0]-sfv[i][1]]                  +sfv[i][0]*sfw[i][0]                  +sfv[i][1]*sfw[i][1]                  ,dp[j]);        if(j>=sfv[i][0]+sfv[i][2])        dp[j]=max(dp[j-sfv[i][0]-sfv[i][2]]                  +sfv[i][0]*sfw[i][0]                  +sfv[i][2]*sfw[i][2]                  ,dp[j]);        if(j>=sfv[i][0]+sfv[i][1]+sfv[i][2])        dp[j]=max(dp[j-sfv[i][0]-sfv[i][1]-sfv[i][2]]                  +sfv[i][0]*sfw[i][0]                  +sfv[i][1]*sfw[i][1]                  +sfv[i][2]*sfw[i][2]                  ,dp[j]);    }    printf("%d\n",dp[N]);}

最长上升子序列
拦截导弹
O(n^2)

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=30000+5;int n;int a[maxn],dp[maxn],dp1[maxn];int main(){    int n=0;    while(scanf("%d",&a[++n])>=1);n--;    for(int i=1;i<=n;i++) dp[i]=dp1[i]=1;    int ans=0,ans1=0;    for(int i=1;i<=n;i++)    for(int j=1;j<i;j++)    {        if(a[i]<=a[j])        dp[i]=max(dp[i],dp[j]+1);        ans=max(ans,dp[i]);        if(a[i]>a[j])        dp1[i]=max(dp1[i],dp1[j]+1);        ans1=max(ans1,dp1[i]);    }    printf("%d\n%d\n",ans,ans1);} 

合唱队形

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=30000+5;int n;int a[maxn],dp[maxn],dp1[maxn];int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    for(int i=1;i<=n;i++) dp[i]=dp1[i]=1;    int ans1=0;    for(int i=1;i<=n;i++)    for(int j=1;j<i;j++)    {        if(a[i]>a[j])        dp[i]=max(dp[i],dp[j]+1);    }    for(int i=n;i>=1;i--)    for(int j=i+1;j<=n;j++)    {        if(a[i]>a[j])        dp1[i]=max(dp1[i],dp1[j]+1);    }    for(int k=1;k<=n;k++)    {        ans1=max(dp[k]+dp1[k]-1,ans1);    }     printf("%d\n",n-ans1);} 

方格取数问题
方格取数

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=11;int mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];int n;int main(){    scanf("%d",&n);    int x,y,v;    memset(mp,0,sizeof(mp));    memset(dp,0,sizeof(dp));    while(scanf("%d%d%d",&x,&y,&v)>=3)    {        if(x==0&&y==0&&v==0) break;        else         {            mp[x][y]=v;        }    }    int sum;    for(int i=1;i<=n;i++)      for(int j=1;j<=n;j++)      {        sum=i+j;        for(int i1=1,j1=sum-i1;i1<=n&&i1+1<=sum;i1++,j1=sum-i1)        {                dp[i][j][i1][j1]=mp[i][j]+mp[i1][j1]+max(max(dp[i-1][j][i1-1][j1],                                                             dp[i][j-1][i1-1][j1]),                                                         max(dp[i-1][j][i1][j1-1],                                                             dp[i][j-1][i1][j1-1]));                if(i==i1&&j==j1) dp[i][j][i1][j1]-=mp[i][j];        }      }    cout<<dp[n][n][n][n]<<'\n';    return 0;}

传纸条
蒟蒻的丑陋代码:

#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int maxn=55;int mp[maxn][maxn],dp[maxn][maxn][maxn][maxn];int m,n;int main(){    scanf("%d%d",&m,&n);    memset(mp,0,sizeof(mp));    memset(dp,0,sizeof(dp));    for(int i=1;i<=m;i++)    for(int j=1;j<=n;j++)      scanf("%d",&mp[i][j]);    int sum;    for(int i=1;i<=m;i++)      for(int j=1;j<=n;j++)      {          sum=i+j;          int i1=1;        for(int j1=sum-i1;i1+1<=sum;i1++,j1=sum-i1)        {            if(i1 > m) continue;//************            if(j1 > n) continue;//************                dp[i][j][i1][j1]=mp[i][j]+mp[i1][j1]+max(max(dp[i-1][j][i1-1][j1],                                                             dp[i][j-1][i1-1][j1]),                                                         max(dp[i-1][j][i1][j1-1],                                                             dp[i][j-1][i1][j1-1]));                if(i==i1&&j==j1) dp[i][j][i1][j1]-=mp[i][j];        }      }    cout<<dp[m][n][m][n]<<'\n';    return 0;}

wzhd大佬的优化版:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>using namespace std;int dp[55][55][55][55],map[55][55];int MAX(int a,int b,int c,int d){    return max(a,max(b,max(c,d)));}int main(){    int m,n;    scanf("%d%d",&m,&n);    for(int i = 1;i <= m;i ++)        for(int j = 1;j <= n;j ++)            scanf("%d",&map[i][j]);    for(int i = 1;i <= m;i ++)        for(int j = 1;j < n;j ++)            for(int k = 1;k <= m;k ++)                for(int l = j + 1;l <= n;l ++)                    dp[i][j][k][l] = MAX(dp[i - 1][j][k - 1][l],dp[i - 1][j][k][l - 1],dp[i][j - 1][k][l - 1],dp[i][j - 1][k - 1][l]) + map[i][j] + map[k][l];    printf("%d",dp[m][n - 1][m - 1][n]);}

最长公共子序列问题
最长公共子序列

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n;const int maxn=3000+5;long a[maxn],b[maxn],dp[maxn][maxn];int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    for(int i=1;i<=n;i++) scanf("%d",&b[i]);    for(int i=1;i<=n;i++)    for(int j=1;j<=n;j++)    {        if(a[i]==b[j]) dp[i][j]=max(dp[i-1][j-1]+1,                                    max(dp[i-1][j],dp[i][j-1]));        else dp[i][j]=max(dp[i-1][j-1],                          max(dp[i-1][j],dp[i][j-1]));    }    printf("%ld\n",dp[n][n]);    return 0;}

买帽子

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>using namespace std;int tot=0,n,dp[505][505];string s,ss;char s1[505];struct Hat{    string a;    int l;}hat[505];bool cmp(Hat A,Hat B){    if(A.l==B.l) return A.a<B.a;    else return A.l>B.l;}int main(){    scanf("%d",&n);    while(n--)    {        s=" ";s1[0]=' ';        cin>>ss;s+=ss;        int len=s.length();        for(int j=1;j<s.length();j++) s1[j]=s[len-j];//exchange        for(int i=1;i<len;i++)             dp[i][len-i]=1;        for(int i=1;i<len;i++)         for(int j=1;j<len;j++)         {            if(s[i]==s1[j])            {                dp[i][j]=max(dp[i-1][j-1]+1,                                    max(dp[i-1][j],dp[i][j-1]));            }            else dp[i][j]=max(dp[i-1][j-1],                          max(dp[i-1][j],dp[i][j-1]));         }        hat[++tot]=(Hat){s,dp[len-1][len-1]};    }    sort(hat+1,hat+1+tot,cmp);    for(int i=1;i<=tot;i++)     {        for(int j=1;j<hat[i].a.length();j++)           cout<<hat[i].a[j];        printf("\n");    }    return 0;} 

编辑距离问题

#include<cstdio>#include<iostream>#include<cstring>using namespace std;string aa,bb;int dp[4005][4005];int main(){    cin>>aa>>bb;    string a=" ",b=" ";    a+=aa,b+=bb;    for(int i=1;i<a.length();i++) dp[i][0]=i;//delete    for(int j=1;j<b.length();j++) dp[0][j]=j;//add    for(int i=1;i<a.length();i++)    for(int j=1;j<b.length();j++)    {        if(a[i]!=b[j])        {            dp[i][j]=min(dp[i-1][j-1]+1,min(dp[i][j-1]+1,dp[i-1][j]+1));        }        else dp[i][j]=dp[i-1][j-1];    }    printf("%d",dp[a.length()-1][b.length()-1]);}

区间DP
石子归并

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n;const int maxn=105;int a[maxn],dp[maxn][maxn],qzh[maxn];int main() {    memset(dp,0x3f3f3f3f,sizeof(dp));    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%d",&a[i]);        dp[i][i]=0;//长度为0的区间         qzh[i]=qzh[i-1]+a[i];    }     for(int i=n;i>=1;i--)      for(int j=i+1;j<=n;j++)        for(int k=i;k<j;k++)//注意不是k=i+1:保证长度为1的区间和为前缀和         {            dp[i][j]=min(dp[i][k]+dp[k+1][j]+qzh[j]-qzh[i-1],dp[i][j]);         }    printf("%d",dp[1][n]);    return 0;}

能量项链

//考试的时候做出来的,开心QwQ#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<queue>using namespace std;int n;int a[400+5];int power[400+5][400+5];int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)     {        scanf("%d",&a[i]);        a[i+n]=a[i];    }    a[2*n+1]=a[1];    for(int i=2*n;i>=1;i--)    for(int j=i+1;j<=2*n;j++)    for(int k=i;k<j;k++)    {        power[i][j]=max(power[i][j],power[i][k]+power[k+1][j]+a[i]*a[k+1]*a[j+1]);    }    int ans=0;    for(int i=1;i<=n;i++) ans=max(ans,power[i][i+n-1]);    printf("%d\n",ans);    return 0;}

乘法游戏

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n;int a[100+5];int dp[105][105];int main(){    memset(dp,0x3f3f3f3f,sizeof(dp));    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%d",&a[i]);        dp[i-1][i]=0;     }    for(int i=n-2;i>=1;i--)      for(int j=i+2;j<=n;j++)        for(int k=i+1;k<j;k++)        {            dp[i][j]=min(dp[i][k]+dp[k][j]+a[i]*a[k]*a[j],dp[i][j]);        }    printf("%d\n",dp[1][n]);    return 0;}

乘积最大
蒟蒻的5 for

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>using namespace std;int n,k,dp[100][100][10],b[100];char a[100];int main(){    scanf("%d%d",&n,&k);getchar();    for(int i=0;i<=n;i++)    {        scanf("%c",&a[i]);        if(i>=1)        {            dp[i][i][0]=b[i]=a[i]-'0';        }    }    int la;    for(int i=n;i>=1;i--)    {        la=10;        for(int j=i+1;j<=n;j++)        {          for(int k=i;k<j;k++)          {            dp[i][j][0]=dp[i][k][0]*la+dp[k+1][j][0];          }        }    }    for(int i=n-1;i>=1;i--)      for(int j=i+1;j<=n;j++)        for(int k=1;k<=(j-i);k++)          for(int l=i;l<j;l++)          {            for(int m=0;k-1-m>=0;m++)            dp[i][j][k]=max(dp[i][l][m]*dp[l+1][j][k-1-m],                            max(dp[i][l][k-1-m]*dp[l+1][j][m],                            dp[i][j][k]));          }    printf("%d\n",dp[1][n][k]);}

wzhd(dalao)改后的4 for及总结

#include<cstdio>#include<iostream>#include<cstring>#include<cmath>using namespace std;int n,k,dp[100][100][10],b[100];char a[100];int main(){    scanf("%d%d",&n,&k);getchar();    for(int i=0;i<=n;i++)    {        scanf("%c",&a[i]);        if(i>=1)        {            dp[i][i][0]=b[i]=a[i]-'0'; // 拆成个位 : 第 i 位         }    }    int la = 10;    for(int i=n;i>=1;i--) //因为我们每次向后去找他的下一位组合起来,因此倒序。         for(int j=i+1;j<=n;j++)//先组成2位数,依次增加。             dp[i][j][0]=dp[i][j-1][0]*la+dp[j][j][0];      for(int i=n-1;i>=1;i--)      for(int j=i+1;j<=n;j++)//同上枚举         for(int m=1;m<=k;m++)// times of‘*’           for(int l=i;l<=j;l++)//枚举断点           {            //枚举左右两区间各有几个“*”。              //其实只要固定左边是1其实都会枚举到的                //方便叙述 (1 * 2 * 3) * 4 /(1 * 2)* (3 * 4) 显然重了,             dp[i][j][m]=max(dp[i][l][m-1]*dp[l+1][j][0],dp[i][j][m]);           }    printf("%d\n",dp[1][n][k]);}

对于石子归并,因为合并的顺序不同会产生不同的结果。

所以石子归并不能规定最后一个合并的对象。

而是将左边合并的结果与右边合并的结果进行合并。

所以我们合并的时候,需要右边的合并结果。

换句话说,我们需要枚举起点而使得我们可以访问到右边的区间。

而乘积最大中,由于乘法满足交换律,我们可以规定最后一个乘的对象。

假设我们规定右边不进行乘法运算,只是将左边的区间乘右边的区间组成的一个数。

而这个数是预处理好的。

由于我们只是将左边合并的结果与右边这一个数合并。

所以我们需要的条件只是从1~i的合并结果,故不需要枚举起点。

总结,合并区间的时候我们并不能盲目地去合并这个区间,而是先观察合并的顺序对合并的结果是否有影响。是否我们需要处理出每一个区间的结果。

记忆化搜索
滑雪

//开全局变量时要慎重,回溯时可能出错 #include<cstdio> #include<iostream>#include<cstring>using namespace std;int n,m;const int maxn=1050;int dp[maxn][maxn],mp[maxn][maxn];int nx[]={0,0,1,-1};int ny[]={1,-1,0,0};int dfs(int x,int y){    if(dp[x][y]) return dp[x][y];    int ans=1;     for(int i=0;i<4;i++)    {        int tx=nx[i]+x,ty=ny[i]+y;        if(tx<=n&&tx>=1&&ty<=m&&ty>=1)         {            if(mp[tx][ty]<mp[x][y])            {                ans=max(ans,dfs(tx,ty)+1);            }        }     }    dp[x][y]=ans;    return ans;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)    for(int j=1;j<=m;j++)    {        scanf("%d",&mp[i][j]);    }    int ans1=1;    for(int i=1;i<=n;i++)    for(int j=1;j<=m;j++)      ans1=max(ans1,dfs(i,j));    cout<<ans1;}

方案数
传球游戏

#include<cstdio>#include<iostream>using namespace std;int n,m;int dp[200][200];int main(){    cin>>n>>m;    dp[0][1]=1;    for(int i=1;i<=m;i++)    for(int j=1;j<=n;j++)    {        if(j==1) dp[i][j]=dp[i-1][n]+dp[i-1][2];        else if(j==n) dp[i][j]=dp[i-1][n-1]+dp[i-1][1];        else dp[i][j]=dp[i-1][j-1]+dp[i-1][j+1];    }    cout<<dp[m][1];}

摆花
二维

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n,m;int a[108],f[108][108];int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)     scanf("%d",&a[i]);    for(int i=0;i<=a[1];i++) f[1][i]=1;    for(int i=2;i<=n;i++)      for(int j=0;j<=m;j++)        for(int k=0;k<=a[i];k++)          if(j-k>=0)            f[i][j]=(f[i][j]+f[i-1][j-k])%1000007;    printf("%d",f[n][m]);}

一维

#include<cstdio>#include<iostream>#include<cstring>using namespace std;int n,m;int a[108],f[108];int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)     scanf("%d",&a[i]);    f[0]=1;    for(int i=1;i<=n;i++)      for(int j=m;j>=1;j--)        for(int k=1;k<=a[i];k++)//**从1开始的原因:自己想          if(j-k>=0)            f[j]=(f[j]+f[j-k])%1000007;    printf("%d",f[m]);}

未完待续

原创粉丝点击