这个题目开始的时候想正向处理,按照给定的点,不断的划分,但是发现状态转移有些小麻烦,就放弃了那种思路,昨天晚上在睡觉之前突然想到,可以逆向处理的,就是将这支树枝先按照指定的点切割,切割后,在组合起来,看组合的时候能使用的最少费用是多少,这样考虑的时候就最优矩阵链问题相同了,状态转移就很清楚了,动规方程如下:f(i , j) = min{ f(i ,k) , f(k+1 , j) + len(i , j)}
其中f(i , j) 表示区间[i , j] 的最优值,len(i , j)表示这一区间的长度总和。
代码如下:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std ;
const int INF = 1000000 ;
int num[55][55] ;
int len[55] ;
int n ;
int length ;
int dp(int , int ) ;
int dist(int , int) ;
int main()
{
// freopen("in.txt" , "r" , stdin) ;
while(cin>>length && length)
{
int i ;
int temp ;
int k ;
temp = 0 ;
scanf("%d" , &n) ;
//转化为段存储,每一段的长度可以求出
for(i = 0 ; i < n ; i ++)
{
scanf("%d" , &len[i]) ;
k = len[i] ;
len[i] = len[i] - temp ;
temp = k ;
}
len[i] = length - temp ;
for(i = 0 ; i <= n ; i ++)
{
for(int j = 0 ; j <= n ; j ++)
{
num[i][j] = INF ;
}
}
cout<<"The minimum cutting is ";
cout<<dp(0 , n)<<"."<<endl ;
}
return 0 ;
}
int dp(int left , int right)//记忆化搜索
{
if(left==right)
return 0 ;
int & ans = num[left][right] ;
if(ans < INF)
return ans ;
for(int i = left ; i <= right ; i ++)
{
if(ans > dp(left , i) + dp(i+1 , right) + dist(left , right) )
ans = dp(left , i) + dp(i+1 ,right) + dist(left , right) ;
}
return ans ;
}
int dist(int i , int j)//求这一区间的总和
{
int sum = 0 ;
while(i <= j)
{
sum += len[i] ;
i ++ ;
}
return sum ;
}
此外需要注意初值要赋为无穷大。
补充采用递推AC的源代码,用递推的时候,要注意阶段的表示,应该以j-i的值递增的顺序来计算。源代码如下:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int INF = 1000000 ;
int num[55][55] ;
int len[55] ;
int n ;
int length ;
int dist(int , int );
int main()
{
//freopen("uva10003.txt" , "r" , stdin) ;
int i ;
int j ;
int k ;
int temp ;
while(cin>>length && length)
{
temp = 0 ;
scanf("%d" , &n) ;
for(i = 0 ; i < n ; i ++)
{
scanf("%d" , &len[i]) ;
k = len[i] ;
len[i] = len[i] - temp ;
temp = k ;
}
len[i] = length - temp ;
for(i = 0 ; i <= n ; i ++)
{
for(j = 0 ; j <= n ; j ++)
{
if(i!=j)
num[i][j] = INF ;
else
num[i][j] = 0 ;
}
}
for(temp = 0 ; temp <= n ; temp ++)//表示j-i的值,从0到n
{
for(i = 0 ; i <= n - temp ; i ++)
{
j = i + temp ;
for(k = 0 ; k <= j ; k ++)
{
if(num[i][j] > num[i][k] + num[k+1][j] + dist(i , j))
num[i][j] = num[i][k] + num[k+1][j] + dist(i , j) ;
}
}
}
printf("The minimum cutting is %d.\n" , num[0][n]) ;
}
return 0;
}
int dist(int i , int j)
{
int sum = 0 ;
while(i <= j)
{
sum += len[i] ;
i ++ ;
}
return sum ;
}