• 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;
    }
  • 相关阅读:
    k8s蓝绿
    nginx总结
    promethues监控 之 TCP连接数
    制作私有ssl证书
    redis命令
    zabbix主机自动发现
    Kubernetes各组件服务重启
    linxu下常用命令
    encodeURIComponent
    查询条件
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2829652.html
Copyright © 2020-2023  润新知