DP(5)--DAG上的DP

矩形嵌套问题
https://www.luogu.com.cn/problem/CF4D

把每个矩形看成一个点,如果一个矩形X能嵌套在矩形Y里面,就可以从X到Y连一条有向边
这样一个有向无环图就建立起来了

设dp[u]表示从u节点出发能够到达的最大长度。
状态转移方程:d[u]=max{d[v]+1|(u,v)€E}

方法1,构建DAG,深搜
#include <iostream>
using namespace std;

struct Rect
{
    int w;
    int h;
    int id;
};

const int MAXN = 5001;
bool g[MAXN][MAXN];
Rect rect[MAXN];
int dp[MAXN];
int cnt = 0;


int f(int u)
{
    //为表项dp[u]声明一个引用res, 任何对res的读写实际上都是在对dp[u]进行
    int& res = dp[u];
    if (res > 0)
        return res;

    res = 1;
    for (int v = 0; v < cnt; ++v)
        if (g[u][v])
            res = max(res, f(v)+1);

    return res;
}

void printpath(int u)
{
    cout << rect[u].id << " ";
    for (int v = 0; v < cnt; ++v)
        if (g[u][v] && dp[u] == (dp[v]+1))
        {
            printpath(v);
            break;
        }
}

int main()
{
    int  i, j, t, W, H, tmp = -1, width, height, n;

    cin >> n >> W >> H;

    for (i = 1; i <= n; ++i)
    {
        cin >> width >> height;
        if (width > W && height > H)
        {
            rect[cnt].w = width;
            rect[cnt].h = height;
            rect[cnt++].id = i;
        }
    }

    if (cnt == 0)
    {
        cout << 0 << endl;
        return 0;
    }

    // Create DAG
    for (i = 0; i < cnt; ++i)
        for (j = 0; j < cnt; ++j)
            if (rect[i].h < rect[j].h && rect[i].w < rect[j].w)
                g[i][j] = true;

    int res = 0;
    for (i = 0; i < n; ++i)
    {
        tmp = f(i);
        if (res < tmp)
        {
            res = tmp;
            t = i;
        }
    }

    cout << res << endl;
    printpath(t);
    cout << endl;

    return 0;
}

方法二: 先排序,在DP
#include <iostream>
#include <stdlib.h>
using namespace std;

struct Rect
{
    int w;
    int h;
    int id;
};

const int MAXN = 5001;
Rect rect[MAXN];
int dp[MAXN];
int from[MAXN];
int cnt = 0;

int cmp(const void *a, const void *b)
{
    return ((Rect *)a)->w - ((Rect *)b)->w;
}

void printpath(int u)
{
    for (int v = 0; v < cnt; ++v)
        if (dp[u] == (dp[v]+1))
        {
            printpath(from[u]);
            break;
        }
    cout << rect[u].id << " ";
}

int main()
{
    int  i, j, t, W, H, width, height, n;

    cin >> n >> W >> H;

    for (i = 1; i <= n; ++i)
    {
        cin >> width >> height;
        if (width > W && height > H)
        {
            rect[cnt].w = width;
            rect[cnt].h = height;
            rect[cnt++].id = i;
        }
    }

    if (cnt == 0)
    {
        cout << 0 << endl;
        return 0;
    }

    for (i = 0; i < cnt; ++i)
    {
        from[i] = -1;
        dp[i] = 1;
    }
    // 按照宽,升序排列,外层物品的宽度大于内层物品的宽度
    qsort(rect, cnt, sizeof(Rect), cmp);
    
    for (i = 1; i < cnt; ++i)
        for (j = 0; j < i; ++j)
            if (rect[i].h > rect[j].h && rect[i].w > rect[j].w && (dp[j]+1) > dp[i])
            {
                dp[i] = dp[j] + 1;
                from[i] = j;
            }

    int res = 0;
    for (i = 0; i < n; ++i)
    {
        if (res < dp[i])
        {
            res = dp[i];
            t = i;
        }
    }

    cout << res << endl;
    printpath(t);
    cout << endl;

    return 0;
}

长方体搭塔,求最塔的最大高度

/* https://vjudge.csgrandeur.cn/problem/UVA-437 */

#include <iostream>
#include <set>
using namespace std;

struct Block
{
    Block(int ll, int ww, int hh) : l(ll), w(ww), h(hh) {}
    // ascending order
    bool operator<(const Block &p) const
    {
        return p.l > l || (p.l == l && p.w > w) || (p.l == l && p.w == w && p.h > h); 
    }
    int l;  // length
    int w;  // width
    int h;  // height
};

int dp[100];

int main()
{
    int i, j, n, L, W, H, cnt = 0, ans;
    while (cin >> n && n > 0)
    {
        ++cnt;
        
        set<Block> blocks;
        for (i = 0; i < n; ++i)
        {
            cin >> L >> W >> H;

            if (L > W)
                blocks.insert(Block(L, W, H));
            else
                blocks.insert(Block(W, L, H));
            if (W > H)
                blocks.insert(Block(W, H, L));
            else
                blocks.insert(Block(H, W, L));
            if (L > H)
                blocks.insert(Block(L, H, W));
            else
                blocks.insert(Block(H, L, W));
        }

        i = 0;
        for (auto iter = blocks.begin(); iter != blocks.end(); ++iter, ++i)
        {
            dp[i] = iter->h;
        }

        auto iter1 = blocks.begin();
        ++iter1;
        i = 1;
        ans = dp[0];
        for (; iter1 != blocks.end(); ++iter1, ++i)
        {
            j = 0;
            for (auto iter2 = blocks.begin(); j < i && iter2 != blocks.end(); ++iter2, ++j)
            {
                if ((iter1->l > iter2->l) && (iter1->w > iter2->w) && (dp[i] < dp[j] + iter1->h))
                {
                    dp[i] = dp[j] + iter1->h;
                }
            }
            
            if (ans < dp[i])
                ans = dp[i];
        }
        cout << "Case " << cnt << ": maximum height = " << ans << endl;
    }

    return 0;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值