10003木棍切割问题

原题详见UVa10003

本文主要包含一下内容:

1.数学模型的建立

2.递推伪代码的推导

3.程序实现

4.总结


关键字: 动态规划     集合


1.数学模型的建立

很多人解不出这道题从根本上是没有读懂题意。我们现在来从头分析一下。

已知:木棍的长度给定为L,切割点数给定为n,而且每个切割点的位置也是固定的。每次切割的花费等于被切割的木棍的长度。

要求:最少的花费

           这一题的关键就是要理解每次切割的花费等于被切割的木棍的长度

           怎么把这句话和切割这个动作联系起来呢?

            我们现在定义(i,j)表示第i个切割点和第j个切割点所产生的木棍。

            现在对这个木棍进行切割,假设切割点为第k个切割点,很显然 i<k<j

            那么木棍(i,j)就变成了两根子木棍(i,k)(k,j),这次切割所产生的花费为c[j]-c[i]  (注:c[i]表示第i个切割点距离左端点的距离)

             定义d[i][j]为切割木棍(i,j)所需最小费用,那么:

             d[i][j] = min{d[i][k]+d[k][j]+c[j]-c[i]}   k:i+1->j-1

              把左又端点分别看成第0个和第n+1个切割点,c[0] =0,c[n+1] =L


2.递推伪代码的推导:

            一问:最初的状态是什么?

               答:最初的状态是不能再切割的子木棍。何为不能再切割的子木棍呢?就是木棍(i,j)之间不再有切割点,

                      具体就是d[0][1]   d[1][2]      d[2][3]        d[3][4] .......d[n][n+1],因为它们是不能再切割的,所以它们的初始值都是0.

                

                      接下来应该计算的就是能被切割一刀的子木棍

                      具体是的d[0][2]   d[1][3]    ........  d[n-1][n+1]

                       接下来是  d[0][3] d[1][4]  .........  d[n-2][n+1]

                

                     最后的结果是d[0][n+1]


            因此 i,j里包含的切割点数p是第一层循环 p:1->n

            i是第二层循环,i:0->n

            k是第三层循环  k:i+1->j-1


伪代码如下:

           memset(d,0,sizeof(d));

           for p:1->n

                do  for i:0->n

                       j = i+p;

                      if j>n+1   break

                     for  k:i+1->j-1

                            d[i][j] = min{d[i][k]+d[k][j]+c[j]-c[i]} 


3.具体代码实现

#include<iostream>
using namespace std;


int n;
int L;
int f[50][50];
int c[50];
const int INF = 99999;

int main(){
	cin >> L;
	cin >> n;

	c[0] = 0;
	c[n + 1] = L;
	for (int i = 1; i <n+1; i++)
		cin >> c[i];

	memset(f, 0, sizeof(f));
	for (int p = 1; p <= n + 1; p++){
		for (int i = 0; i <= n + 1; i++){
			int j = i + p;
			if (j > n + 1) break;

			int min = INF;
			for (int k = i + 1; k < j; k++){
				int t = f[i][k] + f[k][j] + c[j] - c[i];
				if (t < min) min = t;
			}
			if (min != INF)f[i][j] = min;
		}//for i
	}//for p
	

	cout << f[0][n + 1];
	
}


4.总结

大多数的不会做在于根本没有读懂题。

大多数逻辑上的bug在于没有读懂题。

读题读题读题。理解理解理解。

从这题上总结了一下如何写根据状态转移方程写递推程序。

写出状态转移方程和写出具体程序是两回事情,写出状态转移方程到具体程序实现之间还隔着一个伪代码的过程。这个过程对于逻辑复杂的题目很重要!

所以,读题+写伪代码!

             




阅读更多

没有更多推荐了,返回首页