zoukankan      html  css  js  c++  java
  • [UVa-437] The Tower of Babylon

    DAG建模基础;DP与DAG的关系

    传送门:$>here<$

    题意

    n种长方体,各有无限个。可以横竖侧摆放。只有一个长方形的底面,长小于且宽小于令一个底面才可以叠在上面。问最多叠多高?

    数据范围:$n leq 30$

    Solution

    建模

    将木块的6种状态(不是3种)作为不同物体考虑。若a能在b下面,那么连一条a->b的边,权值为b的高。至于第一块的高,考虑使用虚拟点。

    求最长

    已经将所有可能情况归结在图中了。显然这个图一定无环。所以利用DAG的性质,$O(n^2)$求最长路即可。

    DAG求最长路的本质是DP。设$dp[i]$表示从$i$出发的最长路,由于一定不会有环,所以用所有$i$连出去的点进行转移即可。换句话说这些连出去的点和$i$毫无关系,是个独立的子问题。DAG里是可以有重复的子问题的(树就没有)

    透过题解看本质

    何时利用图建模

    二元关系可以利用图来建模。在这道题中,一块木块能否放在另一块上面是一个二元关系。而在建模过程中,每一个木块的状态是唯一的,像这种木块翻转的应当看做两种情况。

    隐式图

    后来想想,其实这道题没有必要建图。因为我们可以对所有木块排序,排序完后就是一个类似LIS的问题了。

    这么想来,$O(n^2)$的LIS做法其实也可以利用DAG来解决,即一个数在另一个数前面且小于后一个,那么连边。求最长路就是LIS。而我们并没有使用建图来解决LIS问题。

    不过这给了我启示,其实DP就是DAG呀!所谓的无后效性,就是无环。

    my code

    注意有6种,而不是3种。底面的长宽也是需要交换的!!!

    当然在代码实现的过程中为了方便依然可以只存三种情况,因为底面长宽交换不影响高,所以可以以两种状态存在,至于放上来的木块,只要横竖以一种状态能够放上就可以。因为一块木块一旦放完,对于剩余的木块它就是底,因此横竖都无所谓。

    /*By DennyQi 2019*/
    #include <cstdio>
    #include <queue>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 10010;
    const int MAXM = 20010;
    const int INF = 0x3f3f3f3f;
    inline int Max(const int a, const int b){ return (a > b) ? a : b; }
    inline int Min(const int a, const int b){ return (a < b) ? a : b; }
    inline int read(){
        int x = 0; int w = 1; register char c = getchar();
        for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
        if(c == '-') w = -1, c = getchar();
        for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
    }
    struct Cuboid{
        int x,y,z;
    }a[30001];
    int n,N,Case,ans;
    int first[30001],nxt[30001],to[30001],cost[30001],cnt,dp[30001],pre[30001];
    bool vis[30001];
    inline bool Nest(int i, int j){
        if((a[i].x>a[j].x && a[i].y>a[j].y) || (a[i].x>a[j].y && a[i].y>a[j].x)) return 1;
        return 0;
    }
    inline void add(int u, int v, int w){
        cost[++cnt] = w, to[cnt] = v, nxt[cnt] = first[u], first[u] = cnt;
    } 
    int Dfs(int u){
        if(vis[u]) return dp[u];
        vis[u] = 1;
        for(int i = first[u]; i; i = nxt[i]){
            if(Dfs(to[i]) + cost[i] > dp[u]){
                dp[u] = dp[to[i]] + cost[i];
                pre[u] = to[i];
            }
        }
        return dp[u];
    }
    int main(){
        while((N = read()) > 0){
            n = N*3;
            memset(vis,0,sizeof(vis));
            memset(dp,0,sizeof(dp));
            memset(first,0,sizeof(first));
            for(int i = 1; i <= N; ++i){
                a[i].x = read(), a[i].y = read(), a[i].z = read();
                a[i+N].x = a[i].x, a[i+N].y = a[i].z, a[i+N].z = a[i].y;
                a[i+N*2].x = a[i].y, a[i+N*2].y = a[i].z, a[i+N*2].z = a[i].x;
            }
            for(int i = 1; i <= n; ++i){
                add(n+1,i,a[i].z);
            }
            for(int i = 1; i <= n; ++i){
                for(int j = 1; j <= n; ++j){
                    if(i == j) continue;
                    if(Nest(i,j)) add(i,j,a[j].z);
                }
            }
            ans = Dfs(n+1);
            ++Case;
            printf("Case %d: maximum height = %d
    ",Case,ans);
        }
        return 0;
    }
  • 相关阅读:
    mysql允许远程访问
    ubuntu pip install MySQL-python mysql_config not found
    ubuntu 阿里云源
    V
    KMP算法之next函数解释(大量的反证法 和数学归纳法来袭)
    日常ACM题目
    F
    J
    中缀表达式求值 ,中缀表达转化为后缀表达式求值,
    数据结构
  • 原文地址:https://www.cnblogs.com/qixingzhi/p/10366778.html
Copyright © 2011-2022 走看看