您的位置:首页 > 其它

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

记忆化搜索:

#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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: