E28【模板】区间DP 石子合并
E28【模板】区间DP 石子合并——信息学竞赛算法_哔哩哔哩_bilibili
[1,1] [1,2] [1,3] [1,4] [1,5]
[2,2] [2,3] [2,4] [2,5]
[3,3] [3,4] [3,5]
[4,4] [4,5]
[5,5]
对角线初值,上三角无穷,上三角转移
// 区间DP O(n^3) #include<bits/stdc++.h> using namespace std; const int N=310; int n,a[N],s[N]; int f[N][N]; //f[i][j]表示把从i到j合并的最小代价 int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i]; memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++) f[i][i]=0; for(int len=2; len<=n; len++) //枚举区间长度 for(int l=1,r=len; r<=n; l++,r++) //枚举区间端点 for(int k=l; k<r; k++) //枚举区间断点 f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]); cout<<f[1][n]; }
// 区间DP O(n^3) #include<bits/stdc++.h> using namespace std; const int N=205; int n,a[N],s[N]; int f[N][N],p[N][N]; void path(int l,int r){ if(l==r){cout<<a[l]; return;} cout<<"("; path(l,p[l][r]); cout<<"+"; path(p[l][r]+1,r); cout<<")"; } void dfs(int l,int r){ if(l==r) return; dfs(l,p[l][r]); dfs(p[l][r]+1,r); cout<<s[r]-s[l-1]<<" "; } signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i]; memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++) f[i][i]=0; for(int len=2;len<=n;len++) for(int l=1,r=len;r<=n;l++,r++) for(int k=l;k<r;k++) if(f[l][r]>f[l][k]+f[k+1][r]+s[r]-s[l-1]) f[l][r]=f[l][k]+f[k+1][r]+s[r]-s[l-1],p[l][r]=k; path(1,n); cout<<endl<<f[1][n]<<endl; dfs(1,n); }
// 区间DP O(n^5) #include<bits/stdc++.h> using namespace std; #define N 20 int n,m,a[N],s[N]; int f[N][N][N]; //f[l,r,t]表示区间[l,r]插入t个*,合并后的最大值 int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",a+i),s[i]=s[i-1]+a[i]; for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) f[i][j][0]=s[j]-s[i-1]; for(int len=2;len<=n;len++) for(int l=1,r=len;r<=n;l++,r++) for(int k=l;k<r;k++) for(int t=1;t<=m;t++) for(int p=0;p<=min(k-l,t);p++){ f[l][r][t]=max(f[l][r][t],f[l][k][p]*f[k+1][r][t-p-1]); if(t-p<r-k) //l~k~r,p=[0,k-l],t-p<r-k f[l][r][t]=max(f[l][r][t],f[l][k][p]+f[k+1][r][t-p]); } printf("%d\n",f[1][n][m]); return 0; }
P3146 [USACO16OPEN] 248 G - 洛谷
// 区间DP O(n^3) #include<bits/stdc++.h> using namespace std; const int N=250; int n,ans,f[N][N]; //f[i,j]表示把从i到j合并获得的最大值 int main(){ scanf("%d",&n); for(int i=1; i<=n; i++){ scanf("%d",&f[i][i]); ans=max(ans,f[i][i]); } for(int len=2; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ for(int k=i; k<j; k++){ if(f[i][k]==f[k+1][j] && f[i][k]){ //两区间f均为0 不转移,例[1 2][3 4] f[i][j]=max(f[i][j],f[i][k]+1); ans=max(ans,f[i][j]); } } } } printf("%d\n",ans); return 0; }
// 区间DP+双指针 O(n^3) #include<bits/stdc++.h> using namespace std; int n,f[405][405],ans; int main(){ cin>>n; for(int i=1; i<=n; ++i){ cin>>f[i][i]; ans=max(ans,f[i][i]); } for(int len=2; len<=n; ++len){ for(int i=1,j=len; j<=n; ++i,++j){ for(int k=i; k<j; ++k){ //合并两个 if(f[i][k] && f[i][k]==f[k+1][j]){ f[i][j]=f[i][k]+f[k+1][j]; break; } } for(int k=i,t=j; k<t-1; ){ //合并三个 if(f[i][j]) break; if(!f[i][k]) ++k; else if(!f[t][j]) --t; else if(f[i][k]==f[t][j]){ if(f[k+1][t-1]) f[i][j]=f[i][k]+f[k+1][t-1]+f[t][j]; else ++k,--t; } else if(f[i][k]<f[t][j]) ++k; else if(f[i][k]>f[t][j]) --t; } ans=max(ans,f[i][j]); } } cout<<ans; }
P2858 [USACO06FEB] Treats for the Cows G/S - 洛谷
满足最优子区间,所以区间DP,每次的断点只有两个
// 区间DP O(n^2) #include<bits/stdc++.h> using namespace std; const int N=2005; int n,a[N],f[N][N]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int len=1; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ f[i][j]=max(f[i+1][j]+a[i]*(n-len+1),f[i][j-1]+a[j]*(n-len+1)); } //len=1时最后一天卖 a*n,len=2时倒数第二天卖 a*(n-1)... } cout<<f[1][n]; }
P1005 [NOIP 2007 提高组] 矩阵取数游戏 - 洛谷
行间独立,每行满足最优子区间,所以区间DP,每次的断点只有两个
// 区间DP O(1000*n^2) #include <bits/stdc++.h> using namespace std; const int N=82; int n,m; __int128 a[N][N],f[N][N],ans; //f[i,j]表示合并区间[i,j]获得的最大值 void input(__int128 &s){ s=0; char c; while(c>'9'||c<'0') c=getchar(); while(c>='0'&&c<='9'){ s=s*10+c-'0'; c=getchar(); } } void output(__int128 x){ if(x>9) output(x/10); putchar(x%10+'0'); } __int128 work(__int128 a[]){ memset(f,0,sizeof(f)); for(int len=1; len<=m; ++len) for(int i=1,j=len; j<=m; ++i,++j) f[i][j]=max(f[i+1][j]+a[i]*((__int128)1<<m-len+1),f[i][j-1]+a[j]*((__int128)1<<m-len+1)); // f[i][j]=max(a[i]+f[i+1][j],f[i][j-1]+a[j])*2; //类似秦九韶 return f[1][m]; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) input(a[i][j]); for(int i=1;i<=n;i++) ans+=work(a[i]); output(ans); }
// 区间DP O(n^2) #include<bits/stdc++.h> using namespace std; const int N=1005; char a[N]; int n,f[N][N]; //f[i,j]把从i到j的字符串变为回文串的最少插入字符数 int main(){ scanf("%s",a+1); n=strlen(a+1); for(int len=2; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ if(a[i]==a[j]) f[i][j]=f[i+1][j-1]; else f[i][j]=min(f[i+1][j],f[i][j-1])+1; } } cout<<f[1][n]; }
// 区间DP O(n^2) #include<bits/stdc++.h> using namespace std; const int N=3005; int n,a[N],f[N][N]; //f[i][j]表示从i到j对称的最小操作次数 int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int len=2;len<=n;len++){ for(int i=1,j=len;j<=n;i++,j++){ if(a[i]==a[j]) f[i][j]=f[i+1][j-1]; else f[i][j]=min(min(f[i+1][j],f[i][j-1]),f[i+1][j-1])+1; } } cout<<f[1][n]; }
P9325 [CCC 2023 S2] Symmetric Mountains - 洛谷
// 区间DP O(n^2) #include<bits/stdc++.h> using namespace std; const int N=5005; int n,a[N]; int f[N][N],ans[N]; int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; memset(ans,0x3f,sizeof ans); ans[1]=0; for(int len=2; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ f[i][j]=f[i+1][j-1]+abs(a[i]-a[j]); ans[len]=min(ans[len],f[i][j]); } } for(int i=1;i<=n;i++) cout<<ans[i]<<" "; }
P6006 [USACO20JAN] Farmer John Solves 3SUM G - 洛谷
// 区间DP O(n^2) #include<bits/stdc++.h> using namespace std; const int N=5010,M=1e6; int n,q,a[N],cnt[3000010]; long long f[N][N]; int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>q; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1; i<=n; i++){ for(int j=i+1; j<=n; j++){ f[i][j]=cnt[a[i]+a[j]+M]; //f[i][j]:a[i]+a[j]+a[i+1~j-1]=0 的个数 cnt[-a[j]+M]++; //cnt:[i+1~j]内值为-a[j]的个数 } for(int j=i+1; j<=n; j++) cnt[-a[j]+M]=0; } for(int len=3; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ f[i][j]=f[i+1][j]+f[i][j-1]-f[i+1][j-1]+f[i][j]; //平面内点的个数容斥 } } while(q--){ int a,b; cin>>a>>b; cout<<f[a][b]<<"\n"; } }
P3102 [USACO14FEB] Secret Code S - 洛谷
// 区间DP O(n^3) #include<bits/stdc++.h> #define mod 2014 using namespace std; const int N=105; char s[N]; int n,d[N][N][N]; //d[i,j,k]表示[i,i+k−1]和[j,j+k−1]是否相等,k为长度 int f[N][N]; //f[i,j]表示区间[i,j]内的方案数 int main(){ scanf("%s",s+1); n=strlen(s+1); for(int i=1; i<=n; i++) for(int j=i; j<=n; j++) for(int k=1; k+i-1<=n && k+j-1<=n; k++) if(s[i+k-1]==s[j+k-1]) d[i][j][k]=1; else break; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) f[i][j]=1; for(int len=3; len<=n; len++) for(int i=1,j=len; j<=n; i++,j++) for(int k=i; k<j; k++){ int a=k-i+1, b=j-k; if(a<b && d[i][k+1][a]) f[i][j]+=f[k+1][j]; //a段=b前段 if(a<b && d[i][j-a+1][a]) f[i][j]+=f[k+1][j]; //a段=b后段 if(a>b && d[i][k+1][b]) f[i][j]+=f[i][k]; //a前段=b段 if(a>b && d[k-b+1][k+1][b]) f[i][j]+=f[i][k]; //a后段=b段 f[i][j]%=mod; } printf("%d",f[1][n]-1); }
P11676 [USACO25JAN] DFS Order P - 洛谷
// 区间DP O(n^3) #include<bits/stdc++.h> using namespace std; const int N=755; int n,c,a[N][N],s[N][N]; int f[N][N]; //f[i,j]表示以i为根的子树包含区间[i,j]的最小代价 int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; for(int j=2; j<=n; j++) for(int i=1; i<j; i++) cin>>a[i][j],c+=a[i][j]<0?-a[i][j]:0; //c所有已有边权之和 for(int i=1; i<=n; i++) for(int j=i+1; j<=n; j++) s[i][j]=s[i][j-1]+(a[i][j]<0?a[i][j]:0); //s[i,j]以i为根的负边权的前缀和 memset(f,0x3f,sizeof f); for(int i=1; i<=n; i++) f[i][i]=0; for(int len=2; len<=n; len++) for(int i=1,j=len; j<=n; i++,j++) for(int k=i; k<j; k++){ f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[i][j]-s[i][k+1]+a[i][k+1]); // printf("[%d %d]=%d\n",i,j,f[i][j]); } cout<<f[1][n]+c; }
浙公网安备 33010602011771号