[UVA 103] Stacking Boxes

  1. 本题目实际上是求DAG上的最长路径。因为盒子之间的嵌套关系是二元关系。而二元关系可以用图模型来建模。例如,盒子a可以嵌套在盒子b中,那么在图模型中就是a和b之间有一条从a到b的有向边。注意到这个有向图是无环的,因为盒子不能直接或间接的嵌套入自己本身。所以建模所得的图是有向无环图DAG。根据题意,只需要对盒子的每个维度进行一次排序,然后判断一个盒子的从小到大的所有维度是否小于另一个盒子的对应维度即可判断这两个盒子是否存在嵌套关系。图在程序中的存储模型使用的是邻接矩阵。算法使用的思想是动态规划。设d(i)表示的是以第i个盒子为嵌套序列尾的该序列长度。则状态转移方程为:d(i) = max{ d(j) + 1 | (j,i)∈E, E为图的有向边集 }。实际实现的时候使用了记忆化的方法。预先定义一个数组d并每一项初始化为0,然后递归调用dp函数来计算。由于当数组d中的元素大于零时,即表示当前的状态已经计算完毕,此时直接返回该值。这样做,有效减少了因不断求解公共子问题所带来的巨大工作量。最后,定义递归函数print_box将所求最大的序列中的盒子序号从前往后输出出来。print_box的算法如下:先搜索可以嵌套入当前盒子cur的盒子j,如果以盒子cur为嵌套序列尾的序列长度是由以盒子j为嵌套序列尾的序列长度计算得来的,那么则打印以盒子j为嵌套序列尾的序列中的盒子(递归),最后再打印当前盒子cur。其实此处可以通过多申请一个parent数组,记录每一个d[i]是由哪一个计算得到的话,可以不用进行邻接矩阵的遍历(空间换时间)。主流程算法:对输入的盒子的维度进行与排序,然后计算以每个盒子为嵌套序列尾的该序列长度,选取最大的一个即可。
  2. 代码如下:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAX 40
    #define DIM 20
    int d[MAX];
    int in[MAX][DIM];
    int graph[MAX][MAX];
    
    int cmp(const void *_a,const void *_b)
    {
        int *a = (int *)_a;
        int *b = (int *)_b;
        return *a<*b?-1:*a>*b?1:0;
    }
    
    bool isNested(int *ba,int *bb,int n)
    {
         int i;
         for(i = 1;i <= n;i++)
             if(ba[i] >= bb[i])
                return false;
         return true;
    }
    
    int dp(int cur,int k)
    {
        int& ans = d[cur];
        if(ans > 0)
           return ans;
        ans = 1;
        for(int j = 1;j <= k;j++)
        {
            if(graph[j][cur])
            {
               ans = ans<dp(j,k)+1?dp(j,k)+1:ans;
            }
        }
        return ans;
    }
    
    void print_box(int cur,int k)
    {
         int j;
         for(j = 1;j <= k;j++)
         {
             if(graph[j][cur] && d[cur] == d[j] + 1)
             {
                print_box(j,k);
                break;
             }
         }
            printf("%d ",cur);
    }
    
    int main()
    {
        int k,n,i,j,max,max_idx;
        while(scanf("%d %d",&k,&n) != EOF)
        {
              for(i = 1;i <= k;i++)
              {
                  for(j = 1;j <= n;j++)
                  {
                      scanf("%d",&in[i][j]);  
                  }
                  qsort(in[i],n+1,sizeof(int),cmp);
              }
              for(i = 1;i <= k;i++)
              {
                  for(j = 1;j <= k;j++)
                  {
                      if(isNested(in[i],in[j],n))
                         graph[i][j] = 1;
                      else
                         graph[i][j] = 0;
                  }
              }
              memset(d,0,sizeof(d));
              max = 0;
              for(i = 1;i <= k;i++)
              {
                  if(max < dp(i,k))
                  {
                     max = dp(i,k);
                     max_idx = i;
                  }
              }
              printf("%d\n",max);
              print_box(max_idx,k);
              printf("\n");
        }
        return 0;
    }
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值