Cutting Sticks UVA - 10003 切木棍 线性dp

题目链接

有一根长度为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 
#include 
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 
#include 
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;
}

 

你可能感兴趣的:(动态规划,算法竞赛入门经典(第二版))