欢迎来到endl的博客hhh☀☾☽♡♥

浏览器标题切换
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

这个DP有点东西

AcWing 271. 杨老师的照相排列

状态表示:f[i][j][k][p][q]即每排分别i、j、k、p、q人,且i>=j>=k>=p>=q

属性:cnt

状态计算:从高到低给学生安排位置,考虑最后一个学生的位置

#include<bits/stdc++.h>
#define ll long long
#define IL inline
#define R register int

using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;

IL int read() {
    int f=1;
    char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    int res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
    return res*f;
}

int n,a[6];
ll f[31][31][31][31][31];

signed main() {
    while(~scanf("%lld",&n)&&n) {
        a[1]=a[2]=a[3]=a[4]=a[5]=0;//Attention!
        for(R i=1;i<=n;++i) a[i]=read();
        memset(f,0,sizeof f);
        f[0][0][0][0][0]=1;
        for(R i=0;i<=a[1];++i)
            for(R j=0;j<=min(a[2],i);++j)
                for(R k=0;k<=min(a[3],j);++k)
                    for(R p=0;p<=min(a[4],k);++p)
                        for(R q=0;q<=min(a[5],p);++q) {
                            ll &x=f[i][j][k][p][q];
                            if(i&&i-1>=j) x+=f[i-1][j][k][p][q];
                            if(j&&j-1>=k) x+=f[i][j-1][k][p][q];
                            if(k&&k-1>=p) x+=f[i][j][k-1][p][q];
                            if(p&&p-1>=q) x+=f[i][j][k][p-1][q];
                            if(q) x+=f[i][j][k][p][q-1];
                        }
        printf("%lld\n",f[a[1]][a[2]][a[3]][a[4]][a[5]]);
    }
    return 0;
}
闫氏DP分析法(免费打广告)

 


 

Acwing 272. 最长公共上升子序列

状态表示:f[i][j]即a[1~i]和b[1~j]的最长公共上升子序列长度,且以b[j]结尾

属性:max

状态转移:

  • 如果a[i]不在最长公共上升子序列中,则f[i][j]=f[i-1][j];
  • 如果a[i]在最长公共上升子序列中(即a[i]==b[j]),则考虑最长公共上升子序列中的倒数第二位是b[1~j-1]中的哪个
#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int

using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;

IL int read() {
  int f=1;
  char ch;
  while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
  int res=ch-'0';
  while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
  return res*f;
}

int n,m,f[3005][3005];//以b[j]结尾 
int a[3005],b[3005],ans;

int main() {
  n=read();
  for(R i=1;i<=n;++i) a[i]=read();
  for(R i=1;i<=n;++i) b[i]=read();
  for(R i=1;i<=n;++i) {
      for(R j=1;j<=n;++j) {
            f[i][j]=f[i-1][j];//不包含a[i]
            if(a[i]==b[j]) {
              f[i][j]=max(f[i][j],1);
                for(R k=1;k<j;++k) {//枚举倒数第二个数 
                    if(a[i]>b[k]) 
                        f[i][j]=max(f[i][j],f[i-1][k]+1);
                }
            }
        }
  }
  for(R i=1;i<=n;++i) ans=max(ans,f[n][i]);
  printf("%d",ans);
  return 0;
}
暴力枚举: Accepted 10/11

仔细观察发现k循环非常多余,每次只多用到了一个b[j-1],故移出、特判

#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int

using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;

IL int read() {
  int f=1;
  char ch;
  while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
  int res=ch-'0';
  while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
  return res*f;
}

int n,m,f[3005][3005];//以b[j]结尾 
int a[3005],b[3005],ans;

int main() {
  n=read();
  for(R i=1;i<=n;++i) a[i]=read();
  for(R i=1;i<=n;++i) b[i]=read();
  for(R i=1;i<=n;++i) {
    int ma=1;
    for(R j=1;j<=n;++j) {
            f[i][j]=f[i-1][j];//不包含a[i]
            if(a[i]>b[j-1]) ma=max(ma,f[i-1][j-1]+1);
            if(a[i]==b[j]) f[i][j]=max(f[i][j],ma);
        }
  }
  for(R i=1;i<=n;++i) ans=max(ans,f[n][i]);//别忘了循环b数组的每一个位置
  printf("%d",ans);
  return 0;
}
优化后代码

 


 

Acwing 274. 移动服务

状态表示:f[i][x][y]表示完成了前i个请求,其中一个员工位于pi,另外两个员工位于x和y时,公司当前最小花费

属性:min

状态计算:

f[i+1,x,y]=min(f[i+1,x,y],f[i,x,y]+c(p1,pi+1))

f[i+1,pi,y]=min(f[i+1,p1,y],f[i,x,y]+c(x,pi+1))

f[i+1,x,pi]=min(f[i+1,x,pi],f[i,x,y]+c(y,pi+1))

#include<bits/stdc++.h>
//#define int long long
#define IL inline
#define R register int

using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;

IL int read() {
  int f=1;
  char ch;
  while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
  int res=ch-'0';
  while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
  return res*f;
}

int n,l,a[205][205],f[1005][205][205],q[1005],ans=inf;

int main() {
  l=read();n=read();
  for(R i=1;i<=l;++i)
    for(R j=1;j<=l;++j) 
        a[i][j]=read();
  for(R i=1;i<=n;++i) q[i]=read();
  memset(f,0x3f,sizeof f);
  f[0][1][2]=0;q[0]=3;
  for(R i=0;i<=n;++i) 
    for(R x=1;x<=l;++x)
        for(R y=1;y<=l;++y) {
            int v=f[i][x][y];
            int z=q[i],w=q[i+1];
            if(x==y||y==z||x==z) continue;
            f[i+1][x][y]=min(f[i+1][x][y],v+a[z][w]);
            f[i+1][z][y]=min(f[i+1][z][y],v+a[x][w]);
            f[i+1][x][z]=min(f[i+1][x][z],v+a[y][w]);
        }
  for(R x=1;x<=l;++x)
        for(R y=1;y<=l;++y) {
            int z=q[n];
            if(x==z||y==z||x==y) continue;      
        ans=min(ans,f[n][x][y]);
    }
  printf("%d",ans);
  return 0;
}
我把p数组写成了q数组

 


 

Acwing 275. 传纸条

状态表示:f[i][x1][x2]即走了i步之后,第一条路径在第x1行,第二条路径在第x2行时取得的数

//x-1+y-1=i,故x1+y1=x2+y2=i+2,只要知道i和x就知道对应的y

属性:max

状态计算:

每条路径有向右、向下两种扩展方法,故共有2*2=4种转移。以两条路径均往右扩展为例,其余三种情况同理。

如果x1=x2并且y1+1=y2+1,那么两条路径进入同一个格子,只累加一次:f[i+1,x1,x2]=max(f[i+1,x1,x2],f[i,x1,x2]+a[x1,y1+1])

否则两条路径分别扩展到不同的格子,两个格子中的数都进行累加:f[i+1,x1,x2]=max(f[i+1,x1,x2],f[i,x1,x2]+a[x1,y1+1]+a[x2,y2+1])

初值为f[0,1,1]=a[1,1]

目标为f[n+m-2,n,n]

——李煜东《算法竞赛进阶指南》

//上面所说的n,m和Acwing题目中的相反

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define IL inline
#define R register int
#define fa(x) a[x].fa
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]

using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;

IL int read() {
    int f=1;
    char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    int res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
    return res*f;
}

int n,a[55][55],f[105][55][55],m;

//f[i][x1][x2]
//i+2=x+y
//i:路径长度
signed main() {
    m=read();n=read();
    for(R i=1;i<=m;++i)
        for(R j=1;j<=n;++j)
            a[i][j]=read();
    f[0][1][1]=a[1][1];
    for(R i=0;i<=n+m+2;++i)
        for(R x1=0;x1<=m;++x1)
            for(R x2=0;x2<=m;++x2) {
                int y1=i+2-x1,y2=i+2-x2;
                if(y1<1||y2<1) continue;//Attention!!
                //down down
                //right right
                if(x1==x2) {
                    f[i+1][x1+1][x2+1]=max(f[i+1][x1+1][x2+1],f[i][x1][x2]+a[x1+1][y1]);
                    f[i+1][x1][x2]=max(f[i+1][x1][x2],f[i][x1][x2]+a[x1][y1+1]);
                }
                else {
                    f[i+1][x1+1][x2+1]=max(f[i+1][x1+1][x2+1],f[i][x1][x2]+a[x1+1][y1]+a[x2+1][y2]);
                    f[i+1][x1][x2]=max(f[i+1][x1][x2],f[i][x1][x2]+a[x1][y1+1]+a[x2][y2+1]);
                }
                //down right
                if(x1+1==x2) f[i+1][x1+1][x2]=max(f[i+1][x1+1][x2],f[i][x1][x2]+a[x1+1][y1]);
                else f[i+1][x1+1][x2]=max(f[i+1][x1+1][x2],f[i][x1][x2]+a[x1+1][y1]+a[x2][y2+1]);
                //right down
                if(x1==x2+1) f[i+1][x1][x2+1]=max(f[i+1][x1][x2+1],f[i][x1][x2]+a[x1][y1+1]);
                else f[i+1][x1][x2+1]=max(f[i+1][x1][x2+1],f[i][x1][x2]+a[x1][y1+1]+a[x2+1][y2]);
            }
    printf("%lld",f[n+m+2][m][m]);
    return 0;
}
冗长的代码

AcWing 273. 分级

难点是证明b数组中的每一个数一定在a数组中出现过

状态表示:f[i][j]即b[1~i]都已经安排好了且b[i]=a'[j](a'[]是排序后的a数组)

属性:min

状态计算:f[i][j]=min(f[i][j],f[i-1][k]+abs(a[i]-a'[j])),k从1~j循环,指b[i-1]对应的是a'[k]

优化方式类似Acwing 272. 最长公共上升子序列

#include<bits/stdc++.h>
#define ll long long
#define IL inline
#define R register int
using namespace std;
const int N=1e6+5,inf=0x3f3f3f3f;

IL int read() {
    int f=1;
    char ch;
    while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
    int res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0';
    return res*f;
}

int n,a[2005],b[2005],ans=inf,f[2005][2005];

int main() {
    n=read();
    for(R i=1;i<=n;++i) b[i]=a[i]=read();
    sort(b+1,b+1+n);//递增
    for(R i=1;i<=n;++i) {
        int mi=inf;
        for(R j=1;j<=n;++j) {
            mi=min(f[i-1][j],mi);
            f[i][j]=mi+abs(a[i]-b[j]);
        }
    }
    for(R i=1;i<=n;++i) ans=min(ans,f[n][i]);
    reverse(b+1,b+1+n);//递减
    for(R i=1;i<=n;++i) {
        int mi=inf;
        for(R j=1;j<=n;++j) {
            mi=min(f[i-1][j],mi);
            f[i][j]=mi+abs(a[i]-b[j]);
        }
    }
    for(R i=1;i<=n;++i) ans=min(ans,f[n][i]);
    printf("%d",ans);
    return 0;
}
Acwing 273.

 

posted @ 2021-03-26 21:20  endl\n  阅读(718)  评论(0编辑  收藏  举报