uva 103 Stacking Boxes

DP经典题目

题意还是比较好懂就是矩形嵌套,不过这个矩阵是m维的,而且嵌套要严格大于。

输入n和m,表示有n个维数为m的超立方体。下面n行每行m个数字,是每个超立方体的信息,即每个维的大小

超立方体的信息可以调整,判断一个超立方体a能不能套住超立方体b是看调整之后能不能

好像(2,6)和(7,3),可以调整为(2,6),(3,7),那么显然(3,7)是能套住(2,6)的

另外能不能套住,还需要每一维都严格大于,好像(2,6),(2,7),由于2相等不能套住

 

其实就是DAG模型,若i能套住j,则g[i][j]=1,然后这个有向图显然是无环的,(要套住必须严格大于,不可能存在互相套住的情况)。相当于求这个DAG上的最长路,dp思想就是  dp[i]=max{ dp[j]+1 };  (i能套住j)  , 这个dp思想用记忆化搜索很容易实现,详细看代码

另外我们把套住这种关系看做一种大于关系(i能套住j则认为i大于j),那么对超立方体内部信息排序后,还要对所有的超立方排序,得到一个“递增”的序列,再对这个递增的序列求LIS,个人觉得这个方法没有求最长路来得简单和直接,所以代码就不写了

 

后来想了另一种dp思想,就是求出任意两点的最长路,即dp[i][j]表示i到j的最长路,然后找到最大的dp[i][j]就是整个图的最长路

dp过程仿照floyd写3个循环。如果i能套住k,k能套住j,那么dp[i][j]=max( dp[i][j] , dp[i][k]+dp[k][j])

这个dp思想其实很好理解,看起来也没什么问题,但是一直wa,几乎想放弃了,然后出了随机数据来测才找到bug

算是逻辑不够严谨,这和整个图的特殊情况也有关系,所以初始化要处理好

1.整个图可以是独立的,根本没有边,也就是说所有超立方体都不能互相嵌套,整个图的最长路是0,顶点个数是1

2.整个图的最长路为1,即只有两两嵌套的情况,那个顶点个数是2。

这个两个特殊情况都有一个共同点,就是整个dp过程都没办法去更新dp[i][j],(因为都不可能找到一个k插进去)
所以更新最大值的语句不能放在  if(i,k && k,j)循环内部,而应该放在3个for里面,即有没有更新dp[i][j]都好,都要拿dp[i][j]更新max

具体看代码

 

 

记忆化搜索AC代码

#include <cstdio>

#include <cstring>

#include <algorithm>

#define N 35

#define M 15

using namespace std;

int dp[N],p[N],g[N][N];

int n,m;

struct box

{

    int a[M];

}b[N];

int ans,x;



int can_nest(int i , int j)  //判断i能不能套住j

{

    for(int k=0; k<m; k++)

        if(b[i].a[k] <= b[j].a[k])

            return 0;

    return 1;

}

void dfs(int i)  //记忆化搜索

{

    if(dp[i]!=-1)

        return ;

    

    dp[i]=1;

    for(int j=1; j<=n; j++)

        if(g[i][j])

        {

            dfs(j);

            if(dp[j]+1>dp[i])

            {

                dp[i]=dp[j]+1;

                p[i]=j;

            }

        }

    if(dp[i]>ans)

    {

        ans=dp[i];

        x=i;

    }

    return ;

}



int main()

{

    while(scanf("%d%d",&n,&m)!=EOF)

    {

        for(int i=1; i<=n; i++)

            for(int j=0; j<m; j++)

                scanf("%d",&b[i].a[j]);



         //对单个超立方体内部的信息排序,超立方体之间不同排序都行

        for(int i=1; i<=n; i++) 

            sort(b[i].a , b[i].a+m);



        memset(g,0,sizeof(g));

        memset(dp,-1,sizeof(dp));

        memset(p,-1,sizeof(p));



        for(int i=1; i<=n; i++)  //建立超立方体间的嵌套关系

            for(int j=1; j<=n; j++)

                if(can_nest(i,j))

                    g[i][j]=1;



        ans=-1; x=0;

        for(int i=1; i<=n; i++) //记忆化搜索

            if(dp[i]==-1)

                dfs(i);



        printf("%d\n",ans);

        int s[N],top;

        memset(s,0,sizeof(s));

        top=-1;

        while(x!=-1)  //用栈来迭代打印路径,递归打印也是可以的

        {

            s[++top]=x;

            x=p[x];

        }

        for(int i=top; i>=0; i--)   //注意路径中最后一个数字不能有空格

            if(i>0) printf("%d ",s[i]);

            else    printf("%d",s[i]);

        printf("\n");



    }

    return 0;

}

 

 

在DAG上仿照floyd的dp,我找遍了所有的解题报告,没有人用这个方法的,算是原创吧……

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

#define N 35

#define M 15

struct box

{

    int a[M];

}b[N];

int n,m;

int dp[N][N],p[N][N];



int can_nest(int i , int j)  //i盒子能套住j盒子

{

    for(int k=0; k<m; k++)

        if(b[i].a[k] <= b[j].a[k])

            return 0;

    return 1;

}

void DP()

{

    memset(dp,0,sizeof(dp));

    memset(p,0,sizeof(p));

    for(int i=1; i<=n; i++)

        for(int j=1; j<=n; j++)

        {

            p[i][j]=j;

            if(can_nest(i,j))

                dp[i][j]=1;

        }



    int max=0,x=1,y=1;  

    //初始化可以应对所有点都是孤立的情况,也就是所有超立方体都不互相嵌套

    for(int k=1; k<=n; k++)

        for(int i=1; i<=n; i++)

            for(int j=1; j<=n; j++)

            {

                if(can_nest(i,k) && can_nest(k,j))

                {

                    if(dp[i][k]+dp[k][j] > dp[i][j])

                    {

                        dp[i][j]=dp[i][k]+dp[k][j];

                        p[i][j]=p[i][k];

                    }

                }

                //这个更新最大值不能放在

                //if(can_nest(i,k) && can_nest(k,j))内部

                //因为可能整个图没有办法找到一个k去更新ij

                //也就是整个图的最长路是1或0,只有两个点或孤立,即一个矩形套住另一个或不能互相嵌套

                if(dp[i][j]>max)

                { max=dp[i][j]; x=i; y=j;}

            }



    max++;  //最长路+1才是顶点个数

    printf("%d\n",max);

    

    //用栈储存路径,当然也可以递归打印

    int stack[N],top; 

    top=0;

    stack[top]=x;

    while(x!=y)

    {

        x=p[x][y];

        stack[++top]=x;

    }

    while(top>=0)

    {

        if(top) printf("%d ",stack[top]);

        else    printf("%d",stack[top]);

        top--;

    }

    printf("\n");



}

int main()

{

    while(scanf("%d%d",&n,&m)!=EOF)

    {

        for(int i=1; i<=n; i++)

            for(int j=0; j<m; j++)

                scanf("%d",&b[i].a[j]);



        for(int i=1; i<=n; i++)

            sort(b[i].a , b[i].a+m);



        DP();

    }

    return 0;

}

 

 

 

你可能感兴趣的:(stack)