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;

}

 

 

你可能感兴趣的:(uva)