题目大意:给一根木棍的长度l,再给n个数c1~cn,表示要在距木棍一端长ci处切一刀,切一刀的代价是当前所切木棍长度。求最小的总代价。
题目分析:仔细一看这其实就是一个石子合并问题,只不过换了一个姿势,一个是合并,一个是切,本质还是一样的。
我将这题转化成石子合并问题dp。
木棍切n刀会产生n+1段,他的逆过程便是将n+1堆石子合并成一堆。
dp[i][j]表示从第i段开始的j段木棍的总代价。那么i到j这一段由i到k这段+k到j这一段,所以可以枚举i到j直接的k,有方程:
dp[i][j] = min(dp[i][k] + dp[i + k][j - k] + sum(i,j),dp[i][j]);(1<= k < j);
dp[i][1] = 第i段原始长度。
注意由于每段原始长度不计入总代价,所以最后要减去原始长度,而原始长度之和正好是一个木棍长度。
详情请见代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 55;
int lcm[N];
int dp[N][N];
int n,l;
int main()
{
int i,j,k;
while(scanf("%d",&l),l)
{
scanf("%d",&n);
lcm[0] = 0;lcm[n + 1] = l;
for(i = 1;i <= n; i ++)
scanf("%d",lcm + i);
sort(lcm + 1,lcm + 1 + n);
memset(dp,1,sizeof(dp));
for(i = 1;i <= n + 1;i ++)
dp[i][1] = lcm[i] - lcm[i - 1];
for(i = 2;i <= n + 1;i ++)//l
{
for(j = 1;i + j <= n + 2;j ++)
{
for(k = 1;k <= i - 1;k ++)
dp[j][i] = min(dp[j][i],dp[j][k] + dp[j + k][i - k] + lcm[i + j - 1] - lcm[j - 1]);
}
}
printf("The minimum cutting is %d.\n",dp[1][n + 1] - l);
}
return 0;
}