这个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;
}
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;
}