第七章:递推、线性dp、背包问题、区间dp(7.27、7.28)

线性dp:

NC16708 过河卒

noip2002的题,注意LL和数组下标不要越界

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL mp[30][30],dp[30][30];
int main()
{
    int n,m,x,y;scanf("%d%d%d%d",&n,&m,&x,&y);
    if(x-1>=0&&y-2>=0)mp[x-1][y-2]=1;
    if(x-2>=0&&y-1>=0)mp[x-2][y-1]=1;
    if(x-1>=0)mp[x-1][y+2]=1;
    if(y-2>=0)mp[x+1][y-2]=1;
    if(x-2>=0)mp[x-2][y+1]=1;
    if(y-1>=0)mp[x+2][y-1]=1;
    mp[x+1][y+2]=1,mp[x+2][y+1]=1;
    mp[x][y]=1;
    dp[0][0]=1;
    dp[0][0]=1;
    for(int i=0;i<=n;i++)
      for(int j=0;j<=m;j++)
      {
        if(!mp[i+1][j])dp[i+1][j]+=dp[i][j];
        if(!mp[i][j+1])dp[i][j+1]+=dp[i][j];
      }
    printf("%lld\n",dp[n][m]);
}
过河卒

NC16619 传球游戏

dp[ i ][ j ]表示传了j次后到达i的方法数

dp[ i ][ j ]=dp[ i-1 ][ j-1 ] + dp[ i+1 ][ j-1 ];

NC16810 [NOIP1999]拦截导弹

求最长的不上升子序列的长度
求原序列最少可划分为多少不上升子序列

NC16664 [NOIP2004]合唱队形

从左向右和从右向左分别求一次最长上升子序列

ans = max(dpl[ i ] + dpr[ i ]) - 1

NC235954 滑雪

记忆化搜索

NC235948 最大子串和

字串和,用f[ i ]去记录,每次a[ i ]和f[ i-1 ]+a[ i ]取max就好

NC235624 牛可乐和最长公共子序列

LCS

背包问题:

二位费用背包:

NC14699 队伍配置

dp]]]costijkATK

先选从者,再选概念礼装

//从者
for(int i=1;i<=n;i++)
{
        int x,y;scanf("%d%d",&x,&y);
        for(int j=d;j>=y;j--)
            for(int k=1;k<=5;k++)
                dp[j][k][0]=max(dp[j][k][0],dp[j-y][k-1][0]+x),ans=max(ans,dp[j][k][0]);
}

//概念礼装
for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        for(int j=d;j>=y;j--)
            for(int k=1;k<=5;k++)
                for(int l=1;l<=k;l++)
                dp[j][k][l]=max(dp[j][k][l],dp[j-y][k][l-1]+x),ans=max(ans,dp[j][k][l]);
}

map优化超大背包:

NC235951 草药大师

 w和v都很大的时候使用map去优化

#include<bits/stdc++.h>
#define LL long long
using namespace std;
map<int,LL>mp,tmp;
map<int,LL>::iterator it;
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    mp.clear();mp[0]=0;
    for(int i=1;i<=n;++i)
    {
        int v,w;scanf("%d%d",&w,&v);
        tmp.clear();
        for(it=mp.begin();it!=mp.end();++it)
        {
            int x=it->first;
            LL y=it->second;
            if(tmp.find(x)==tmp.end())tmp[x]=y;
            else tmp[x]=max(tmp[x],y); 
            if(x+w<=m)tmp[x+w]=max(tmp[x+w],y+v);
        }
        mp.clear();
        LL ans=-1;
        for(it=tmp.begin();it!=tmp.end();++it)
          if(it->second>ans)
          {
              mp[it->first]=it->second;
              ans=it->second;
          }
        if(i==n)printf("%lld\n",ans);
    }
}
草药大师

区间dp:

区间DP是先枚举长度,再枚举起点,然后在起点和终点之间枚举中间点,表示合并的两个区间

NC50493 石子合并

dp[ i ][ j ]为合并[ i , j ]区间的最小得分

dp[ i ][ j ] = min(dp[ i ][ k ] + dp[ k+1 ][ j ] + sum( i , j ))

求max同理

因为是环形,所以记得断环乘2

NC235246 田忌赛马

贪心+区间dp

解析

#include<bits/stdc++.h>
#define LL long long
using namespace std;
bool cmp(const int x,const int y){return x>y;}
int a[5003],b[5003],dp[5003][5003];
int check(int x,int y)
{
    if(x>y)return 200;
    if(x==y)return 0;
    return -200;
}
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;++i)scanf("%d",&b[i]);
    sort(b+1,b+1+n);
    memset(dp, -0x3f, sizeof(dp));
    for(int len=1;len<=n;++len)
      for(int i=1;i+len-1<=n;++i)
      {
          int j=i+len-1;
          int k=len;
          if(len==1)dp[i][j]=check(a[i],b[k]);
          else dp[i][j]=max(dp[i+1][j]+check(a[i],b[k]),dp[i][j-1]+check(a[j],b[k]));
      }
    printf("%d\n",dp[1][n]);
}
田忌赛马

NC13230 合并回文子串

dp[ i ][ j ][ k ][ l ] A的[ i,j ]和B的[ k,l ]能不能组成回文串

转移的时候两两匹配(四种情况),还要关注中间的是否是回文串

#include<bits/stdc++.h>
#define LL long long 
using namespace std;
char a[55],b[55];
int dp[53][53][53][53];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int ans=0;
        memset(dp,0,sizeof(dp));
        scanf("%s",a+1);
        scanf("%s",b+1);
        int l1=strlen(a+1);
        int l2=strlen(b+1);
        for(int d1=0;d1<=l1;++d1)
          for(int d2=0;d2<=l2;++d2)
            for(int i=1,j=d1;j<=l1;++i,++j)
              for(int k=1,l=d2;l<=l2;++k,++l)
              {
                  if(d1+d2<=1)dp[i][j][k][l]=1;
                  else
                  {
                      dp[i][j][k][l]=0;
                      if(a[j]==a[i]&&d1>1)
                        if(dp[i+1][j-1][k][l])
                          dp[i][j][k][l]=1;
                      if(a[j]==b[k]&&d1&&d2)
                        if(dp[i][j-1][k+1][l])
                          dp[i][j][k][l]=1;
                      if(b[l]==a[i]&&d1&&d2)
                        if(dp[i+1][j][k][l-1])
                          dp[i][j][k][l]=1;
                      if(b[l]==b[k]&&d2>1)
                        if(dp[i][j][k+1][l-1])
                          dp[i][j][k][l]=1;
                }
                if(dp[i][j][k][l])ans=max(ans,d1+d2);
              }
        printf("%d\n",ans);
    }
}
合并回文子串

NC16645 [NOIP2007]矩阵取数游戏

可以看出每一行之间是不相关的,转化为行内的区间dp问题

注意会爆long long,用__int128

#include<bits/stdc++.h>
#define LL __int128
using namespace std;
LL a[100],dp[100][100];
int n,m;
void print(LL x)
{
    if(x<0){putchar('-');x=-x;}
    if(x/10)print(x/10);
    putchar(x%10+'0');
}
LL ksm(LL x,LL y)
{
    LL res=1;
    while(y)
    {
        if(y&1)res*=x;
        x=x*x;y/=2;
    }
    return res;
}
LL work()
{
    for(int i=1;i<=m;++i)
      for(int l=1,r=i;r<=m;++l,++r)
        dp[l][r]=max(dp[l+1][r]+a[l]*ksm(2,m-i+1),dp[l][r-1]+a[r]*ksm(2,m-i+1));
    return dp[1][m];
}
int main()
{
    scanf("%d%d",&n,&m);
    LL ans=0;
    for(int i=1;i<=n;++i)
    {
        memset(dp,0,sizeof(dp));
        for(int j=1;j<=m;++j)
            scanf("%lld",&a[j]);
          ans+=work();
    }
    print(ans);
        
}
矩阵取数

NC207781 迁徙过程中的河流

先时间从小到大排序

dp[i]表示前i个人已过河的最短时间

前i-1个人已经过河,就让第一个把船开回去再一起回来

前i-2个人已经过河,就让1先开回去,把最后两个人送回来后,2再过去把1接回来

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL dp[100005],t[100005];
int main()
{
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%lld",&t[i]);
    sort(t+1,t+1+n);
    dp[1]=t[1];
    dp[2]=t[2];
    for(int i=3;i<=n;++i)
      dp[i]=min(dp[i-1]+t[1]+t[i],dp[i-2]+t[i]+t[1]+2*t[2]);
    printf("%lld\n",dp[n]);
}
迁徙过程中的河流
posted @ 2022-08-25 21:47  yyys  阅读(23)  评论(0编辑  收藏  举报