Cutting Sticks UVA - 10003 切木棍 线性dp

本文探讨了在给定棍子长度和切割点的情况下,如何通过动态规划找到切割棍子的最优顺序,以最小化总切割成本。提供了两种实现方法,一种是递推法,另一种是记忆搜索法,展示了算法的具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

有一根长度为L(L<1000)的棍子,还有n(n<50)个切割点的位置(按照从小到大排列)。你的任务是在这些切割点的位置处把棍子切成n+1部分,使得总切割费用最小。每次切割的费用等于被切割的木棍长度。例如,L=10,切割点为2, 4, 7。如果按照2, 4, 7的顺序,费用为10+8+6=24,如果按照4, 2, 7的顺序,费用为10+4+6=20。
【分析】
设d(i,j)为切割小木棍i~j的最优费用,则,边界条件为:if(i == j -1) d(i,j) = 0。其中最后一项a[j]-a[i]代表第一刀的费用。切完之后,小木棍变成i~k和k~j两部分,状态转移方程由此可得。把切割点编号为1~n,左边界编号为0,右边界编号为n+1,则答案为d(0,n+1)。状态有O(n2)个,每个状态的决策有O(n)个,时间复杂度为O(n3)。值得一提的是,本题可以用四边形不等式优化到O(n2),有兴趣的读者请参见本书的配套《算法竞赛入门经典——训练指南》或其他参考资料。

方法一:按照j-i递增的顺序递推

#include <iostream>
#include <cstring>
using namespace std; 
const int N = 50+5, INF = 0x3f3f3f3f;
int point[N], d[N][N];
int main(int argc, char** argv) {	
	int len;
	while(cin>> len && len){
		int n;
		cin>> n;
		for(int i = 1; i <= n; i++)	cin>> point[i];
		point[0] = 0; point[n+1] = len;
		for(int i = 0; i <= n; i++) d[i][i+1] = 0;//len==1时的边界。
		for(int len = 2; len <= n+1; len++){
			for(int i = 0, j= i+len; j <= n+1; i++, j++){
				d[i][j] = INF;
				for(int k = i+1; k < j; k++)
					d[i][j] = min(d[i][j], d[i][k]+d[k][j]+point[j]-point[i]);
			}
		}	
		cout<<"The minimum cutting is "<< d[0][n+1]<< ".\n";
	} 	
	return 0;
}

方法二:记忆搜索

#include <iostream>
#include <cstring>
using namespace std; 
const int N = 50+5, INF = 0x3f3f3f3f;
int point[N], d[N][N];
 
int dp(int i,int j){
//	if(i == j-1) return 0; 
	if(d[i][j] != -1) return d[i][j];
	int &ans = d[i][j] = INF;
	for(int k = i+1; k < j; k++)
		ans = min(ans, dp(i,k)+dp(k,j)+point[j]-point[i]);
	return ans ;  
}
 
int main(int argc, char** argv) {	
	int len;
	while(cin>> len && len){
		int n;
		cin>> n;
		for(int i = 1; i <= n; i++)	cin>> point[i];
		point[0] = 0; point[n+1] = len;
		memset(d,-1,sizeof(d));
		for(int i = 0; i <= n+1; i++) d[i][i+1] = 0;//直接在外设定边界,减少在dp中的判断。 
		cout<<"The minimum cutting is "<< dp(0,n+1)<< ".\n";
	} 	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值