UVA-10003 Cutting Sticks
2015-12-01 01:00
381 查看
Brief description
给定一根长为l的木头,要求在木头的n个位置锯断它,每次锯断一段木头,就把这段木头的长度累加起来输出。
Algorithm analyse
这是我接触的第一道区间dp的题目。类似紫书里所说的最优矩阵链乘。网上看别人的代码发现还类似于石头合并问题。这个没接触。
首先困扰我的是二维dp[i][j]中的i,j表示什么,首先自然想到既然是区间,肯定是区间的起点和终点,但是这样的话,因为并不是遍历区间中的所有点。所有状态转移过程很难写,然后网上看了题解,是根据节点的所代表的相对位置来区分,比如 25 50 75 共5个节点
为 0 25 50 75 100 相对位置为0,1,2,3,4,5.这样就很方便.
一个大致的状态转移方程不难写.dp[i][j]=min(dp[i][k]+dp[k][j]+c[j]-c[i] ,(i<k<j))。记忆化搜索很容易,但是问题又来了,最优子结构不好表示.
请看紫书中最优矩阵链乘的一句话:正确的方法是按照j-i递增的顺序递推,因为长区间的值依赖于短区间的值。意思是说从短区间开始转移。也就是有一个区间大小的for循环.
Code
记忆化搜索:
递推:
注意:
a.在递推关系中,我预处理了区间为1的情况,也就是相邻的2个节点。即不可再分割的木块。
b.在最后一次转移中,多算了一次c[n+1]-c[0],也就是原长,所以要减去.
小白书还有一种n^2的算法,到时再看。
Summary
1.对于区间dp,现在有个粗略的理解,也就是根据节点的变法来反应区间的变化。
2.对于dp,临界条件,以及0的分析,以及结果的分析,一定要细致入微,上题中没有减去原长卡了我很久.还是看别人代码才发现自己的错误ORZ。
给定一根长为l的木头,要求在木头的n个位置锯断它,每次锯断一段木头,就把这段木头的长度累加起来输出。
Algorithm analyse
这是我接触的第一道区间dp的题目。类似紫书里所说的最优矩阵链乘。网上看别人的代码发现还类似于石头合并问题。这个没接触。
首先困扰我的是二维dp[i][j]中的i,j表示什么,首先自然想到既然是区间,肯定是区间的起点和终点,但是这样的话,因为并不是遍历区间中的所有点。所有状态转移过程很难写,然后网上看了题解,是根据节点的所代表的相对位置来区分,比如 25 50 75 共5个节点
为 0 25 50 75 100 相对位置为0,1,2,3,4,5.这样就很方便.
一个大致的状态转移方程不难写.dp[i][j]=min(dp[i][k]+dp[k][j]+c[j]-c[i] ,(i<k<j))。记忆化搜索很容易,但是问题又来了,最优子结构不好表示.
请看紫书中最优矩阵链乘的一句话:正确的方法是按照j-i递增的顺序递推,因为长区间的值依赖于短区间的值。意思是说从短区间开始转移。也就是有一个区间大小的for循环.
Code
记忆化搜索:
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (0x3f3f3f3f) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int maxn=55; int c[maxn],dp[maxn][maxn]; int l,n; int mem_srh(int s,int e) { if(s==e-1) return 0; int& ans=dp[s][e]; if(ans) return ans; ans=-1; for(int i=s+1;i<=e-1;i++) { int v=mem_srh(s,i)+mem_srh(i,e)+c[e]-c[s]; if(ans<0||ans>v) ans=v; } return ans; } int main() { while(scanf("%d",&l)&&l) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&c[i]); c[0]=0; c[n+1]=l; clr(dp); printf("The minimum cutting is %d.\n",mem_srh(0,n+1)); } return 0; }
递推:
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) #define eps (1e-10) #define INF (0x3f3f3f3f) #define clr(x) memset((x),0,sizeof (x)) #define cp(a,b) memcpy((a),(b),sizeof (b)) typedef long long ll; typedef unsigned long long ull; typedef pair<int,int> P; const int maxn=55; int dp[maxn][maxn]; int c[maxn]; int l,n; int main() { while(scanf("%d",&l)&&l) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&c[i]); c[0]=0; c[n+1]=l; for(int i=0;i<=n+1;i++) { for(int j=i;j<=n+1;j++) { if(j-i==1) dp[i][j]=c[j]-c[i]; else dp[i][j]=INF; } dp[i][i]=0; } for(int k=2;k<n+2;k++) { for(int i=0;i<=n+1-k;i++) { int j=i+k; for(int tpk=i+1;tpk<j;tpk++) dp[i][j]=min(dp[i][j],dp[i][tpk]+dp[tpk][j]+c[j]-c[i]); } } printf("The minimum cutting is %d.\n",dp[0][n+1]-l); } return 0; }
注意:
a.在递推关系中,我预处理了区间为1的情况,也就是相邻的2个节点。即不可再分割的木块。
b.在最后一次转移中,多算了一次c[n+1]-c[0],也就是原长,所以要减去.
小白书还有一种n^2的算法,到时再看。
Summary
1.对于区间dp,现在有个粗略的理解,也就是根据节点的变法来反应区间的变化。
2.对于dp,临界条件,以及0的分析,以及结果的分析,一定要细致入微,上题中没有减去原长卡了我很久.还是看别人代码才发现自己的错误ORZ。
相关文章推荐
- 点击图片传值到text 尚未解决
- Java回调函数
- 程序中断和调用子程序有何区别
- Unity游戏开发——自动为动画剪辑添加事件 之 最后几帧的事件不能被调用的问题
- 字符串单模板匹配学习笔记(一)kmp算法
- 【计算机网络】:pop,IMAP,SMTP协议的区别与联系
- sql 取30-40行记录
- android-第一行代码
- 解决Ubuntu下的Error: can't find libjava.so问题
- Angular 学习笔记——自定义标签
- CUDA与OpenGL交互
- 迷宫求解
- 【数据结构】建立和平衡AVL二叉树
- Swing界面开发总结
- JavaWeb学习总结(一)——JavaWeb开发入门
- AngularJS Best Practices: Directory Structure
- 浅析linux服务端socket编程
- ” Java中使用split分隔字符串
- 迷宫求解问题-递归(栈的应用)
- vim color