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; }
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; }
仔细观察发现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; }
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; }