UVA-10003 Cutting Sticks

来源:互联网 发布:nginx md5函数 编辑:程序博客网 时间:2024/05/16 18:15

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

记忆化搜索:

#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。


0 0
原创粉丝点击