CF-Tinkoff Challenge-Elimination Round-D-Presents in Bankopolis

ACM模版

描述

描述

题解

没怎么做过树归问题,所以当做到这道题时不免有些懵逼。

典型的树归问题,从一个始发点不断查找下一个可行解,最后输出最小的可行解总和即可。

这么说是不是太随意了一些啊,毕竟我没有做出这道题。这个题并不算难,能想到动归就好办了。

首先我们设置一个 dp[st][ed][k] 表示从 st 点出发能到达的某个方向最远处 ed 点且剩余 k 个点未走,既然说到某一个方向,那么这里肯定是正向和反向,针对于正向和反向的不同还要有分别的状态转移方程,存图设置一个 mp[u][v],记住这里是有向图,搞成无向图会在第四组数据 WA,如果读得清楚题,自然知道是有向图,而我就是读不懂题的人。

设置完上边两个数组,剩下的也就是一个递归搜索,打个比方说,也就是如果从 st 在某一个方向最远可以到达 ed,那么这个区间中的任意一个都可以到达,假如此时到达了 mid,那么下一次分别是可以从 mid 到 st 和 mid 到 ed,这个具体不好说,还是看代码吧,一看就理解了,嘻嘻(^__^) 嘻嘻……

对了,考虑到边界问题,上述从 st 到达 ed 都是不包括 st 和 ed 的,也就是说 (st,ed) 开区间。

强大的树归!!!

代码

#include <iostream>
#include <cstring>
#include <algorithm>

#define POSTIVE 1
#define REBERSE 0

using namespace std;

const int MAXN = 88;
const int INF = 0x3f3f3f3f;

int dp[MAXN][MAXN][MAXN];
int mp[MAXN][MAXN];

int n, k, m;
int u, v, c;

void init()
{
    memset(dp, -1, sizeof(dp));
    memset(mp, 0x3f, sizeof(mp));
}

int getDP(int st, int ed, int k, int dir)
{
    if (dp[st][ed][k] != -1)
    {
        return dp[st][ed][k];
    }
    if (k == 0)
    {
        dp[st][ed][k] = 0;
        return dp[st][ed][k];
    }
    int res = INF;
    if (dir)
    {
        for (int i = st + 1; i < ed; i++)
        {
            if (mp[st][i] != INF)
            {
                res = min(res, mp[st][i] + getDP(i, st, k - 1, REBERSE));
                res = min(res, mp[st][i] + getDP(i, ed, k - 1, POSTIVE));
            }
        }
    }
    else
    {
        for (int i = ed + 1; i < st; i++)
        {
            if (mp[st][i] != INF)
            {
                res = min(res, mp[st][i] + getDP(i, st, k - 1, POSTIVE));
                res = min(res, mp[st][i] + getDP(i, ed, k - 1, REBERSE));
            }
        }
    }

    dp[st][ed][k] = res;
    return dp[st][ed][k];
}

int main(int argc, const char * argv[])
{
//    freopen("/Users/zyj/Desktop/input.txt", "r", stdin);

    init();

    scanf("%d%d%d", &n, &k, &m);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d%d", &u, &v, &c);
        mp[u][v] = min(mp[u][v], c);  //  可能会有重边,重边只保留最短边
    }

    int res = INF;
    for (int i = 1; i <= n; i++)        //  以任何一个点为起点
    {
        res = min(res, getDP(i, 0, k - 1, REBERSE));
        res = min(res, getDP(i, n + 1, k - 1, POSTIVE));
    }

    if (res == INF)
    {
        cout << "-1\n";
    }
    else
    {
        cout << res << '\n';
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值