UVA - 10003 Cutting Sticks(区间DP)

传送门


此问题类似于最优矩阵链乘,只不过将子问题之间的乘法换成了相互递推的求和

状态转移

d ( i , j ) d(i,j) d(i,j)为切割小木棍 i − j i-j ij的最优费用,则:

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 ik k − j k-j kj两部分。把左边界编号为 0 0 0,右边界编号为 n + 1 n+1 n+1,答案为 d ( 0 , n + 1 ) d(0,n+1) d(0,n+1)

值得商榷的地方

  1. 因为引入了两边界,因此之前的 j = i + l e n − 1 j=i+len-1 j=i+len1变成了 j = i + l e n j=i+len j=i+len
  2. 如果按之前的模板,花费代价为 a [ j + 1 ] − a [ j ] a[j+1]-a[j] a[j+1]a[j],最后答案应该是 d ( 0 , n ) d(0,n) d(0,n)
  3. 本题可以用四边形不等式优化,可以看罗勇军老师的博客帮助学习
  4. 但是套用四边形不等式区间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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值