题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=114&page=show_problem&problem=944
题目大意:给定一根长为l的木头,要求在木头的n个位置锯断它,每次锯断一段木头,就把这段木头的长度累加起来输出。
解题思路:如果不转换模型,这题很难想。顺着每次吧长度为li的木头锯成两段,将li累加,而后如果某段木头中间还需锯断,再累加lj,这样就相当于要去搜索,难做复杂度高。换个角度,其实每次累加就是两段的长度累加起来。原来最后几根中间不可以再锯的木头现在设想成最初的状态,如果两个木头可以合并,那么就把两个木头长度累加,现在问题转换为经典的石子合并问题,也就是区间合并问题。状态转移方程为:dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j])(dp[i][j]表示从木头的i位置到j位置的累加和最少为多少,sum[i][j]表示i到j的长度)
如果这题的木头锯断位置不是有序的,那么模型更难转换,那更有意思。
测试数据:
100 3 25 50 75
10 4 4 5 7 8
100
1
50
代码:
#include <stdio.h>
#include <string.h>
#define MAX 100
#define INF 1000000000
int n,l,arr[MAX];
int dp[MAX][MAX];
int sum[MAX][MAX];
int main()
{
int i,j,k,tpk,tpsum;
while (scanf("%d",&l),l) {
scanf("%d",&n);
arr[0] = 0,arr[n+1] = l;
for (i = 1; i <= n; ++i)
scanf("%d",&arr[i]);
n = n + 1;
memset(sum,0,sizeof(sum));
for (i = 1; i <= n; ++i) {
for (j = i; j <= n; ++j) {
dp[i][j] = INF;
sum[i][j] = arr[j] - arr[i-1];
}
dp[i][i] = 0;
}
for (k = 1; k < n; ++k) {
//k为区间长度
for (i = 1; i <= n - k; ++i) {
//i表示第一个区间的开始位置
j = i + k;
for (tpk = i; tpk < j; ++tpk) {
//tp+1表示另一个区间的开始位置
if (dp[i][tpk] + dp[tpk+1][j] + sum[i][j] < dp[i][j])
dp[i][j] = dp[i][tpk] + dp[tpk+1][j] + sum[i][j];
}
}
}
printf("The minimum cutting is %d.\n",dp[1][n]);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。