动态规划题。切棍子,类似矩阵连乘问题(Matrix Multiplication problem)。
当时我先写了一个比较简洁的递归代码,但是超时了。。。超时代码如下,虽然超时,但可以很好的体现出状态转移:
#define INF 50000
int c[50];
int dp(int left, int right, int cleft, int cright)
{
if(cleft==cright) return 0;
int min = INF;
int tmp;
for(int i=cleft; i<cright; i++)
{
tmp = dp(left,c[i],cleft,i)+dp(c[i],right,i+1,cright)+(right-left);
if(min>tmp) min = tmp;
}
return min;
}
int main()
{
int L,N;
while(cin >> L && L)
{
cin >> N;
for(int i=0; i<N; i++) cin >> c[i];
cout << "The minimum cutting is " << dp(0,L,0,N) << ".\n";
}
return 0;
}
dp的参数left,right表示当前处理的棍子左右端点在原棍子中的位置,cleft,cright表示当前这段棍子中需要切开的位置的编号(即c[cleft],c[cright]是要切开的位置)。
这段代码超时后想到需要记忆化,但dp有四个参数,难道要开四维数组?再想想发现left,right参数与cleft,cright参数的设置有点重复的感觉,因为棍子切开的位置就是下次处理切开后两段棍子的端点位置。于是在c数组的左右将0和L加入,看作0位置和L位置也是需要切开的位置,这样只需要cleft和cright两个参数就可以了。
于是稍稍改变一下超时代码得到了以下的AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define INF 50000
int c[52];
int mincost[52][52];
int dp(int i, int j)
{
if(mincost[i][j]!=-1) return mincost[i][j];
int min = INF, tmp;
for(int k=i+1; k<j; k++)
{
tmp = dp(i,k)+dp(k,j)+(c[j]-c[i]);
if(min>tmp) min = tmp;
}
return mincost[i][j] = min;
}
int main()
{
int L,N;
while(cin >> L && L!=0)
{
cin >> N;
for(int i=1; i<=N; i++) cin >> c[i];
c[0] = 0; c[N+1] = L;
memset(mincost,-1,sizeof(mincost));
for(int i=0; i<=N; i++)
mincost[i][i+1] = 0;//相邻两切口间的棍子不需要再切开,费用为0
cout << "The minimum cutting is " << dp(0,N+1) << ".\n";
}
return 0;
}
mincost数组元素mincost[i][j]表示从i号切口到j号切口的那段棍子处理完毕需要的最小费用,它同时也是dp(i,j)的返回值。所求结果即dp(0,N+1)。