题目链接: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原创,但可以转载,因为我们是兄弟。