Uva | Cutting Sticks

2024-08-25 17:58
文章标签 sticks uva cutting

本文主要是介绍Uva | Cutting Sticks,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原题

You have to cut a wood stick into pieces. The most affordable company, The Analog Cutting Machinery,Inc. (ACM), charges money according to the length of the stick being cut. Their procedure of work requires that they only make one cut at a time.
It is easy to notice that different selections in the order of cutting can led to different prices. For example, consider a stick of length 10 meters that has to be cut at 2, 4 and 7 meters from one end.
There are several choices. One can be cutting first at 2, then at 4, then at 7. This leads to a price of 10 + 8 + 6 = 24 because the first stick was of 10 meters, the resulting of 8 and the last one of 6.
Another choice could be cutting at 4, then at 2, then at 7. This would lead to a price of 10 + 4 + 6 = 20, which is a better price.
Your boss trusts your computer abilities to find out the minimum cost for cutting a given stick.

Sample Input
100
3
25 50 75
10
4
4 5 7 8
0
Sample Output
The minimum cutting is 200.
The minimum cutting is 22.

最近在看DP,看到了区间DP的相关内容,于是便自己动手推了推一些东西,这样对这个了解可以深刻一点。

思考过程

首先给出的几个切点跟它整体的长度并没有什么关系,而且对于一个切点来说,它所产生的影响是这一段的总长度而不是切点的位置。
所以在尝试列了这么一个表格之后无果:
用例是10,切点2、4、7
使用dp[i][j]表示状态转移
表格1
是的,以切点为阶段列出矩阵,发现根本无法正确求解。
切点0【位置为2】和0产生了10,1和1产生了10,然后想再往下推导发现无从下手。

错误之处在于这根本就不是一个可以进行递推的状态,下一状态和当前状态的关系并没有显现出来,也就无法进行DP了。

然后突然想到是不是可以对切之后的小块长度进行分析;
表格2
对于长度为10,切点为2、4、7的情况可以转化为上述图形所表示的情境。
不过当时还是没有脱离切的次数作为阶段,这个思想;
于是列出了错误的下表:
表格3
如上图,[0][0]表示取第一段,[0][1]表示取第1,2段…然后….
还是不对,这样一来肯定是10啊。

问题出在切木棍的时候,是会重复计算某一段的。
而像我所列的这个式子,在[0][2]的时候,并没有去判断子问题,也没有算当前切割会产生什么效果(╯‵□′)╯︵┻━┻

所以就在思考划分子问题(/(ㄒoㄒ)/~~ 这才是正确DP思路啊喂之前在想什么呢)

正确的思维开始

我们逆向思维,考虑将一根根短木棍拼接成一个长木棍的情况。
表格4
表格2
那么如上图,取第0块和第1块,第1块和第2块,第2块和第3块的代价很容易计算出来是4,5,6;

然后想到Tushar Roy的课程中,包括计算最小矩阵连乘次数的时候,他所使用的方式似乎都是一个个小区间做。
在此安利一下这个视频,需翻墙:https://www.youtube.com/watch?v=vgLJZMUfnsU&list=PLrmLmBdmIlpsHaNTPP_jHHDx_os9ItYXr&index=3

回顾一下在矩阵链乘时候他的做法【虽然我和他的推导方式不太一样但是实质都是区间DP】:
表格4
对于这道题,我们也可以用类似的方法:
表格5
当len=2的时候,可以得到相应的长度为4,5,6;
同理,len=3的时候
表格6
看到这一步,状态的转移就非常明显了,而我们也可以就此继续推导到剩余所有的值。而dp[0][3]即是我们要求的整个木棍的分割产生的价值量。

整理一下思路重新出发:
对于长度为10,切点为2、4、7的木棍
可以将其切分为2、2、3、3一共4段
其DP方程为:
dp[i][j]=0 if i==j 显然单段木棍是不不需要切分不会产生cost
sum[i~j]+min{dp[i][k]+dp[k+1][j]} if i<=k < j
即当需要切分的时候,所需要的cost为整段木棍的长度【sum】和其子问题切分所需要的cost。

完美收官。

代码

对于DP的方式,我一开始是采取同我所写的矩阵链乘同样的求法,采用逆推的方式
表格7

因为dp方程里面,dp[i][j]所依赖的状态dp[i][k]、dp[k+1][j]必须出现在[i][j]之前,所以要想得出它们必须得倒推才能得出正确的结果;
代码如下:

for(int i=n;i>=0;i--){for(int j=i+1;j<n;j++){int min=INT_MAX;//需要计算i~j的总量int sum=0;for(int k=i;k<=j;k++) sum+=woods[k];for(int k=i;k<j;k++){if(dp[i][k]+dp[k+1][j]<min)min=dp[i][k]+dp[k+1][j];}dp[i][j]=sum+min;}
}

在参考了网上的一篇博客之后:
参考链接:http://www.cnblogs.com/zsboy/archive/2013/03/08/2950261.html

感觉写成这样才更加符合区间DP的性质

//遍历长度 
for(int len=1;len<=n;len++){//长度不可以超过总长 for(int i=0;i<n && i+len<n;i++){int j=i+len;//结束点//需要计算i~j的总量int sum=0;int min=INT_MAX;for(int k=i;k<=j;k++) sum+=woods[k];//状态转移for(int k=i;k<j;k++){if(dp[i][k]+dp[k+1][j]<min)min=dp[i][k]+dp[k+1][j];}dp[i][j]=sum+min;}
}

感觉这和很久以前看的石子合并问题如出一辙【当时太渣完全看不懂在说什么】,只是石子合并的问题没有那个sum选项而已。
于是我们引入

区间DP

区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,
将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值
设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价
最小区间F[i,i]=0(一个数字无法合并,∴代价为0)

每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段

For p:=1 to n do // p是区间长度,作为阶段。
for i:=1 to n do // i是穷举的区间的起点
begin
j:=i+p-1; // j是 区间的终点,这样所有的区间就穷举完毕
if j>n then break; // 这个if很关键。
for k:= i to j-1 do // 状态转移,去推出 f[i,j]
f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] }
end;
这个结构必须记好,这是区间动态规划的代码结构

小结

一道题的解题报告写了2个小时,在不明白什么是区间DP的情况下【虽然看过无数的概念但是之前还是完全懵逼】,一点点分析和解决问题。
这些碰壁的经验对于读者可能没有什么价值,但是对于自己的价值还是蛮大的。
初学DP,也谈不上什么经验,但是觉得如果自己独立推导出来,可以对这些阶段、状态、决策有着更加深入的认识。

这篇关于Uva | Cutting Sticks的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1106252

相关文章

uva 10055 uva 10071 uva 10300(水题两三道)

情歌两三首,水题两三道。 好久没敲代码了为暑假大作战热热身。 uva 10055 Hashmat the Brave Warrior 求俩数相减。 两个debug的地方,一个是longlong,一个是输入顺序。 代码: #include<stdio.h>int main(){long long a, b;//debugwhile(scanf("%lld%lld", &

poj 3259 uva 558 Wormholes(bellman最短路负权回路判断)

poj 3259: 题意:John的农场里n块地,m条路连接两块地,w个虫洞,虫洞是一条单向路,不但会把你传送到目的地,而且时间会倒退Ts。 任务是求你会不会在从某块地出发后又回来,看到了离开之前的自己。 判断树中是否存在负权回路就ok了。 bellman代码: #include<stdio.h>const int MaxN = 501;//农场数const int

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

uva 10387 Billiard(简单几何)

题意是一个球从矩形的中点出发,告诉你小球与矩形两条边的碰撞次数与小球回到原点的时间,求小球出发时的角度和小球的速度。 简单的几何问题,小球每与竖边碰撞一次,向右扩展一个相同的矩形;每与横边碰撞一次,向上扩展一个相同的矩形。 可以发现,扩展矩形的路径和在当前矩形中的每一段路径相同,当小球回到出发点时,一条直线的路径刚好经过最后一个扩展矩形的中心点。 最后扩展的路径和横边竖边恰好组成一个直

uva 10061 How many zero's and how many digits ?(不同进制阶乘末尾几个0)+poj 1401

题意是求在base进制下的 n!的结果有几位数,末尾有几个0。 想起刚开始的时候做的一道10进制下的n阶乘末尾有几个零,以及之前有做过的一道n阶乘的位数。 当时都是在10进制下的。 10进制下的做法是: 1. n阶位数:直接 lg(n!)就是得数的位数。 2. n阶末尾0的个数:由于2 * 5 将会在得数中以0的形式存在,所以计算2或者计算5,由于因子中出现5必然出现2,所以直接一

uva 568 Just the Facts(n!打表递推)

题意是求n!的末尾第一个不为0的数字。 不用大数,特别的处理。 代码: #include <stdio.h>const int maxn = 10000 + 1;int f[maxn];int main(){#ifdef LOCALfreopen("in.txt", "r", stdin);#endif // LOCALf[0] = 1;for (int i = 1; i <=

uva 575 Skew Binary(位运算)

求第一个以(2^(k+1)-1)为进制的数。 数据不大,可以直接搞。 代码: #include <stdio.h>#include <string.h>const int maxn = 100 + 5;int main(){char num[maxn];while (scanf("%s", num) == 1){if (num[0] == '0')break;int len =

uva 10014 Simple calculations(数学推导)

直接按照题意来推导最后的结果就行了。 开始的时候只做到了第一个推导,第二次没有继续下去。 代码: #include<stdio.h>int main(){int T, n, i;double a, aa, sum, temp, ans;scanf("%d", &T);while(T--){scanf("%d", &n);scanf("%lf", &first);scanf

uva 10916 Factstone Benchmark(打表)

题意是求 k ! <= 2 ^ n ,的最小k。 由于n比较大,大到 2 ^ 20 次方,所以 2 ^ 2 ^ 20比较难算,所以做一些基础的数学变换。 对不等式两边同时取log2,得: log2(k ! ) <=  log2(2 ^ n)= n,即:log2(1) + log2(2) + log2 (3) + log2(4) + ... + log2(k) <= n ,其中 n 为 2 ^

uva 10025 The ? 1 ? 2 ? ... ? n = k problem(数学)

题意是    ?  1  ?  2  ?  ...  ?  n = k 式子中给k,? 处可以填 + 也可以填 - ,问最小满足条件的n。 e.g k = 12  - 1 + 2 + 3 + 4 + 5 + 6 - 7 = 12 with n = 7。 先给证明,令 S(n) = 1 + 2 + 3 + 4 + 5 + .... + n 暴搜n,搜出当 S(n) >=