此问题类似于最优矩阵链乘,只不过将子问题之间的乘法换成了相互递推的求和
状态转移
设 d ( i , j ) d(i,j) d(i,j)为切割小木棍 i − j i-j i−j的最优费用,则:
d ( i , j ) = m i n ( d ( i , j ) , d ( i , k ) + d ( k , j ) + a [ j ] − a [ i ] ) , i < k < j d(i,j)=min(d(i,j),d(i,k)+d(k,j)+a[j]-a[i]),i<k<j d(i,j)=min(d(i,j),d(i,k)+d(k,j)+a[j]−a[i]),i<k<j
其中最后一项 a [ j ] − a [ j ] a[j]-a[j] a[j]−a[j]代表第一次切的费用,切完之后小木棍变成 i − k i-k i−k和 k − j k-j k−j两部分。把左边界编号为 0 0 0,右边界编号为 n + 1 n+1 n+1,答案为 d ( 0 , n + 1 ) d(0,n+1) d(0,n+1)
值得商榷的地方
- 因为引入了两边界,因此之前的 j = i + l e n − 1 j=i+len-1 j=i+len−1变成了 j = i + l e n j=i+len j=i+len
- 如果按之前的模板,花费代价为 a [ j + 1 ] − a [ j ] a[j+1]-a[j] a[j+1]−a[j],最后答案应该是 d ( 0 , n ) d(0,n) d(0,n)
- 本题可以用四边形不等式优化,可以看罗勇军老师的博客帮助学习
- 但是套用四边形不等式区间DP优化的板子,上面的状态转移应该也要变化,也就是要按原来的板子来写而不是刘汝佳老师思路,可以参考这个博客看一下
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=1005;
int n;
int a[maxn];
int d[1005][1005];
int f(int i,int j){ //记忆化搜索
int &ans=d[i][j];
if(ans<inf) return ans;
if(i==j || j==i+1) ans=0;
else{
for(int k=i+1;k<j;k++){
ans=min(ans,f(i,k)+f(k,j)+a[j]-a[i]);
}
}
return ans;
}
int main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int L;
while(cin>>L && L){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
a[0]=0,a[n+1]=L;
memset(d,0x3f,sizeof d);
for(int i=0;i<=n;i++){
d[i][i]=0;
d[i][i+1]=0; //两个相邻区间的花费也为0
}
d[n+1][n+1]=0;
for(int len=2;len<=n+1;len++)
for(int i=0;i<=n+1-len;i++){
int j=i+len;
for(int k=i+1;k<j;k++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]+a[j]-a[i]);
}
cout<<"The minimum cutting is "<<d[0][n+1]<<".\n";
//cout<<"The minimum cutting is "<<f(0,n+1)<<".\n";
}
return 0;
}