這個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.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM