8.14总结
区间 DP
拆分型区间 DP 是一种用于解决序列分割问题的动态规划方法。核心思想是将一个区间拆分成若干子区间,分别求解子问题,再通过合并子问题的解得到原问题的解。关键特征如下:
- 问题结构:问题涉及线性序列(如数组、字符串),需要将序列分割成连续子段进行处理。
- 状态定义:通常定义
dp[l][r]表示区间[l, r]的最优解。 - 转移方式:枚举分割点
k,将区间拆分为[l, k]和[k+1, r],通过子区间解合并得到当前解。 - 求解顺序:按区间长度从小到大递推,先求解小区间,再求解大区间。
- 初始化:最小子区间(如
l=r)直接初始化。
时间复杂度通常为 \(O (n^3)\)。
T1: P1775 石子合并(弱化版)
题目简述
有 N 堆石子排成一列,每次合并相邻两堆,代价为两堆石子质量之和,求最小总合并代价。
详细思路
- 状态定义:
dp[l][r]表示合并区间[l, r]的最小代价。 - 初始化:
- 单堆石子:
dp[i][i] = 0(无需合并)。
- 单堆石子:
- 前缀和:预处理
sum[i]为前i堆石子质量和,快速计算区间和sum[r] - sum[l-1]。 - 状态转移:
- 枚举区间长度
len从 2 到 N。 - 枚举起点
l,计算终点r = l + len - 1。 - 枚举分割点
k(l ≤ k < r),将区间拆分为[l, k]和[k+1, r]。 - 转移方程:
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k+1][r] + sum[r] - sum[l-1])
- 枚举区间长度
- 结果:
dp[1][N]为全局最小代价。 - 时间复杂度:\(O(n^3)\)。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,dp[maxn][maxn],a[maxn];
int sum[maxn];
signed main(){
cin>>n;
memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=1;i<=n;i++){
cin>>a[i];
dp[i][i]=0;
sum[i]=sum[i-1]+a[i];
}
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<r;k++){
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+sum[r]-sum[l-1]);
}
}
}
cout<<dp[1][n];
return 0;
}
T2: P1622 释放囚犯
题目简述
P 个牢房排成一列,释放 Q 个指定囚犯。每次释放时,与该囚犯所在连通块相邻的所有囚犯会发怒(需送肉)。求最小送肉人次。
详细思路
- 问题转化:
- 释放点将牢房序列分割为 (Q + 1) 个连续段(如释放点
[3,6,14]分割为[1-2], [4-5], [7-13], [15-20])。 - 定义连续段结构体
Node{ l, r },存储每个段的左右端点。
- 释放点将牢房序列分割为 (Q + 1) 个连续段(如释放点
- 状态定义:
dp[i][j]表示处理第i到第j个连续段的最小代价。 - 初始化:
dp[i][i] = 0(单个连续段无需操作)。 - 状态转移:
- 枚举区间长度
len从 2 到 (Q + 1)。 - 枚举起点
lt,计算终点rt = lt + len - 1。 - 枚举分割点
k(lt ≤ k < rt),合并子区间[lt, k]和[k+1, rt]。 - 转移方程:
dp[lt][rt] = min(dp[lt][rt], dp[lt][k] + dp[k+1][rt] + a[rt].r - a[lt].l)- 合并代价
a[rt].r - a[lt].l表示当前大区间总囚犯数减 1(如[1,20]代价为20-1=19)。
- 合并代价
- 枚举区间长度
- 结果:
dp[1][Q+1]为最小送肉人次。 - 时间复杂度:\(O(Q^3)\)。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=1e3+5,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int p,dp[maxn][maxn];
struct Node{
int l,r;
}a[maxn];
int q;
int sum[maxn];
signed main(){
cin>>p>>q;
a[0].r=-1;
for(int i=1;i<=q;i++){
int x;
cin>>x;
a[i].l=a[i-1].r+2;
a[i].r=x-1;
}
a[q+1].l=a[q].r+2;
a[q+1].r=p;
int n=q+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dp[i][j]=inf;
}
dp[i][i]=0;
}
for(int len=2;len<=n;len++){
for(int lt=1;lt+len-1<=n;lt++){
int rt=lt+len-1;
for(int k=lt;k<rt;k++){
dp[lt][rt]=min(dp[lt][rt],dp[lt][k]+dp[k+1][rt]+a[rt].r-a[lt].l);
}
}
}
cout<<dp[1][q+1];
return 0;
}
T3: UVA348 Optimal Array Multiplication Sequence
题目简述
给定 n 个矩阵的行列尺寸,通过括号化确定乘法顺序,使得标量乘法总次数最小。
详细思路
- 状态定义:
dp[i][j]表示计算矩阵链[i, j]的最小乘法次数。d[i][j]记录最优分割点 k(用于输出括号化方案)。
- 初始化:
dp[i][i] = 0(单个矩阵无需乘法)。 - 状态转移:
- 枚举区间长度
len从 2 到 n。 - 枚举起点
l,计算终点r = l + len - 1。 - 枚举分割点
k(l ≤ k < r),将链拆分为[l, k]和[k+1, r]。 - 转移方程:
cost = dp[l][k] + dp[k+1][r] + a[l].x * a[k].y * a[r].y
若cost < dp[l][r],则更新dp[l][r] = cost并记录d[l][r] = k。
- 枚举区间长度
- 输出方案:递归函数
print(l, r):- 若
l == r,输出"A" + l。 - 否则,在分割点
k = d[l][r]处递归左右子区间,并添加括号。
- 若
- 时间复杂度:\(O(n^3)\)。
代码
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int maxn=17,mod=1e9+7,inf=1e18;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();return x*f;}
void write(int x){if(x<0){putchar('-'),x=-x;}if(x>9){write(x/10);}putchar(x%10+'0');return;}
int fpow(int a,int b,int p){if(b==0){return 1;}int res=fpow(a,b/2,p)%p;if(b%2==1){return((res*res)%p*a)%p;}else{return(res*res)%p;}}
int n,dp[maxn][maxn],d[maxn][maxn];
struct Node{
int x,y;
}a[maxn];
void print(int l,int r){
if(l==r){
cout<<"A"<<l;
return;
}
int k=d[l][r];
cout<<"(";
print(l,k);
cout<<" x ";
print(k+1,r);
cout<<")";
}
signed main(){
int tmp=0;
while(cin>>n){
if(n==0) break;
tmp++;
memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y;
dp[i][i]=0;
}
for(int len=2;len<=n;len++){
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<r;k++){
int cost=dp[l][k]+dp[k+1][r]+a[l].x*a[k].y*a[r].y;
if(cost<dp[l][r]){
dp[l][r]=cost;
d[l][r]=k;
}
}
}
}
cout<<"Case "<<tmp<<": ";
print(1,n);
cout<<endl;
}
return 0;
}
本人(KK_SpongeBob)蒟蒻,写不出好文章,但转载请注明原文链接:https://www.cnblogs.com/OIer-QAQ/p/19038619

浙公网安备 33010602011771号