uva 10003 Cutting Sticks

DP(类似于矩阵链乘法)

我们以sample来说明问题

长度100,切的地方为25,50,75.

那么无论怎么切最后都是剩下四段 [0,25] , [25,50] , [50,75] , [75,100]

把它们连续的两两合并的话就能得到上一层的状态,好像[0,25]和[25,50]合并,就得到[0,50],也就是说从[0,50]变到[0,25] , [25,50],花费是多少呢,就是50

所以说到这里,也差不多了,状态转移方程为 dp[i][j]=min{ dp[i][k]+dp[k][j]+(y-x)}  (k是i和j之间的一个数)

这样子的话,我们的目标状态就是dp[0][len],即原来的整一条的费用。

这个时候无论是用递推还是用记忆化搜索去做都非常形象,非常好懂。

另外dp[i][j]里面的ij表示的确切的长度,而数据中木棒的长度上限为1000,所以dp数组大小要为dp[1000][1000]。当然可以优化一下空间,同时也提高了一些时间

dp[i][j]的i和j表示数据中的第i刀和第j刀,这样其实也起到了表示长度的作用,因为数据说明中说了切的地方是严格递增的序列。所以dp数组的大小只需要开到dp[50][50] , 因为最多切50刀

记忆化搜索(0.500s)

#include <cstdio>
#include <cstring>
#define M 60
#define N 1010
#define INF 0x3f3f3f3f
int a[M],dp[N][N];
int n,m;

int min(int i ,int j)
{ return i<j?i:j; }

void dfs(int x , int y)
{
    if(dp[x][y]!=INF)
        return ;
    for(int i=1; a[i]<y; i++)
        if(a[i]>x)
        {
            dfs(x,a[i]);
            dfs(a[i],y);
            dp[x][y]=min(dp[x][y] , dp[x][a[i]]+dp[a[i]][y]+(y-x) );
        }
    return ;
}
int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        scanf("%d",&m);
        a[0]=0;
        for(int i=1; i<=m; i++)
            scanf("%d",&a[i]);
        a[m+1]=n;
        //memset(dp,0x3f,sizeof(dp));
        for(int i=0; i<=m; i++)
            dp[a[i]][a[i+1]]=0;
        for(int l=2; l<=m+1; l++)
            for(int i=0; i<=m-l+1; i++)
                dp[a[i]][a[i+l]]=INF;

        dfs(0,n);
        printf("The minimum cutting is %d.\n",dp[0][n]);
    }
    return 0;
}

递推(0.108s)

#include <cstdio>
#include <cstring>
#define M 60
#define N 1010
#define INF 0x3f3f3f3f
int a[M],dp[N][N];
int n,m;

int min(int i ,int j)
{ return i<j?i:j; }
int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        scanf("%d",&m);
        a[0]=0; a[m+1]=n;
        for(int i=1; i<=m; i++)
            scanf("%d",&a[i]);
        //memset(dp,0x3f,sizeof(dp));  
        //并不是所有状态都要清INF,否则会TLE
        for(int i=0; i<=m; i++)
            dp[a[i]][a[i+1]]=0;

        for(int l=2; l<=m+1; l++)  //把多少块合并
            for(int i=0; i<=m-l+1; i++) //标号
            {
                int j=i+l , x=a[i] , y=a[j];
                dp[x][y]=INF;
                for(int k=i+1; k<j; k++)
                    dp[x][y]=min(dp[x][y] , dp[x][a[k]]+dp[a[k]][y]+y-x);
            }
        printf("The minimum cutting is %d.\n",dp[0][n]);
    }
    return 0;
}

递推优化加速(0.072s)(只是修改了一些微小的地方,思想和上面的递推是完全一样的)

#include <cstdio>
#include <cstring>
#define M 60
#define N 1010
#define INF 0x3f3f3f3f
int a[M],dp[M][M];
int n,m;

int min(int i ,int j)
{ return i<j?i:j; }
int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        scanf("%d",&m);
        a[0]=0; a[m+1]=n;
        for(int i=1; i<=m; i++)
            scanf("%d",&a[i]);
        //memset(dp,0x3f,sizeof(dp));  
        //并不是所有状态都要清INF,否则会TLE
        for(int i=0; i<=m; i++)
            dp[i][i+1]=0;

        for(int l=2; l<=m+1; l++)  //把多少块合并
            for(int i=0; i<=m-l+1; i++) //标号
            {
                int j=i+l,Min=INF;
                for(int k=i+1; k<j; k++)
                    Min=min(Min , dp[i][k]+dp[k][j]);
                dp[i][j]=Min+a[j]-a[i];
            }
        printf("The minimum cutting is %d.\n",dp[0][m+1]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/scau20110726/p/2833906.html