E28【模板】区间DP 石子合并
E28【模板】区间DP 石子合并——信息学竞赛算法_哔哩哔哩_bilibili
[1,1] [2,2] [3,3] [4,4] [5,5] 
[1,2] [2,3] [3,4] [4,5] 
[1,3] [2,4] [3,5] 
[1,4] [2,5] 
[1,5]
[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(){ memset(f,0x3f,sizeof(f)); cin>>n; for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[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[j]-s[i-1]); cout<<f[1][n]; }
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]<<" "; }
P11838 [USACO25FEB] Printing Sequences B - 洛谷
// 区间DP O(n^4) #include<bits/stdc++.h> using namespace std; const int N=110; int T,n,m,a[N],f[N][N]; bool check(int l,int r,int k){ //k是否循环节 for(int i=l;i<=r-k;i++) if(a[i]!=a[i+k]) return false; return true; } int main(){ cin>>T; while(T--){ memset(f,0x3f,sizeof f); cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i],f[i][i]=1; 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]); for(int k=1;k<j;k++){ // for(int k=1;k*k<=len;k++){ if(len%k!=0) continue; if(check(i,j,k)) f[i][j]=min(f[i][j],f[i][i+k-1]); // if(check(i,j,len/k)) f[i][j]=min(f[i][j],f[i][i+len/k-1]); } } } cout<<(f[1][n]<=m?"YES\n":"NO\n"); } return 0; }
P5851 [USACO19DEC] Greedy Pie Eaters P - 洛谷
// 区间DP O(n^3) #include<bits/stdc++.h> using namespace std; const int N=310; int n,m; int f[N][N],g[N][N][N]; int main(){ scanf("%d%d",&n,&m); for(int i=1,w,l,r; i<=m; i++){ scanf("%d%d%d",&w,&l,&r); for(int j=l; j<=r; j++) g[l][r][j]=w; } for(int len=1; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ for(int k=i; k<=j; k++) g[i][j][k]=max(g[i][j][k],max(g[i+1][j][k],g[i][j-1][k])); } } for(int len=1; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ for(int k=i; k<=j; k++) f[i][j]=max(f[i][j],f[i][k-1]+g[i][j][k]+f[k+1][j]); } } printf("%d\n",f[1][n]); return 0; }
P3080 [USACO13MAR] The Cow Run G/S - 洛谷
// 区间DP O(n^2) #include<bits/stdc++.h> using namespace std; const int N=1005; int n,c,d[N],f[N][N][2]; //f[i,j,0/1]是套住[i,j]的牛,站在了i点/j点的最少的花费 int main(){ cin>>n; for(int i=1; i<=n; i++) cin>>d[i]; d[++n]=0; sort(d+1,d+1+n); c=lower_bound(d+1,d+1+n,0)-d; memset(f,0x3f,sizeof f); f[c][c][1]=f[c][c][0]=0; for(int len=2; len<=n; len++){ for(int i=1,j=len; j<=n; i++,j++){ int cnt=n-(j-i); //没套住的牛数 f[i][j][0]=min(f[i+1][j][0]+(d[i+1]-d[i])*cnt, //从i+1走到i的花费 f[i+1][j][1]+(d[j]-d[i])*cnt); //从j走到i的花费 f[i][j][1]=min(f[i][j-1][0]+(d[j]-d[i])*cnt, //从i走到j的花费 f[i][j-1][1]+(d[j]-d[j-1])*cnt);//从j-1走到j的花费 } } cout<<min(f[1][n][0],f[1][n][1]); return 0; }
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号
浙公网安备 33010602011771号