uva10269_SPFA思想+DP转移

8 篇文章 0 订阅
8 篇文章 0 订阅

题目

题目大意:有n个村庄和m个城堡,编号从1到n+m,给出一个有权图,没有自环和重边。马里奥走一个单位距离需要一个单位时间。起点为城堡n+m,终点为乡村1。马里奥可以瞬移K次,每次瞬移不超过距离L,且起点和终点必须为村庄或城堡、到达一个城堡时必须结束当前瞬移。求到达村庄1的最小花费时间。

详见uva10269

解题思路

D P [ i ] [ j ] [ k ] DP[i][j][k] DP[i][j][k]表示到达城市 i i i,还剩 j j j次瞬移机会,上次瞬移机会还剩下 k k k单位距离可以使用。分类讨论,状态过多,具体看代码注释。

结合最短路的思想,只有一个点被松弛成功了,这个点才压入队列。

代码

#include <bits/stdc++.h>
using namespace std;

int dp[110][12][510]; //dp[i][j][k]表示到了第i号位置 还剩j次瞬移机会 上次的瞬移还剩下k单位能使用 的最小花费

vector<pair<int, int>> mp[110];

int A, B, M, L, K;
bool vis[110];
void bfs(int s)
{
    dp[s][K][0] = 0;
    queue<int> q;
    q.push(s);
    while (q.size())
    {
        int pos = q.front();
        q.pop();
        vis[pos] = 0;
        for (auto it : mp[pos])
        {
            int v = it.first;
            int w = it.second;
            for (int j = 0; j <= K; j++) //剩余j次瞬移机会
            {
                for (int k = 0; k <= L; k++) //上次的瞬移还剩下k单位
                {
                    if (k >= w) //这段距离小于上次剩下的瞬移k
                    {
                        if (pos <= A) //上一个不是城堡 继续使用上次瞬移
                        {
                            if (dp[v][j][k - w] > dp[pos][j][k])
                            {
                                dp[v][j][k - w] = dp[pos][j][k];
                                if (!vis[v])
                                    q.push(v), vis[v] = 1;
                            }
                        }
                        else //上一个是城堡,考虑当前要不要使用瞬移
                        {
                            if (dp[v][j][L - w] > dp[pos][j + 1][k]) //使用瞬移
                            {
                                dp[v][j][L - w] = dp[pos][j + 1][k];
                                if (!vis[v])
                                    q.push(v), vis[v] = 1;
                            }
                            if (dp[v][j][0] > dp[pos][j][k] + w) //不使用瞬移
                            {
                                dp[v][j][0] = dp[pos][j][k] + w;
                                if (!vis[v])
                                    q.push(v), vis[v] = 1;
                            }
                        }
                    }
                    else
                    {
                        if (w > L) //只能老老实实走
                        {
                            if (dp[v][j][0] > dp[pos][j][k] + w)
                            {
                                dp[v][j][0] = dp[pos][j][k] + w;
                                if (!vis[v])
                                    q.push(v), vis[v] = 1;
                            }
                        }
                        else
                        {
                            if (dp[v][j][0] > dp[pos][j][k] + w) //老老实实走
                            {
                                dp[v][j][0] = dp[pos][j][k] + w;
                                if (!vis[v])
                                    q.push(v), vis[v] = 1;
                            }
                            if (dp[v][j][L - w] > dp[pos][j + 1][k]) //再使用一次瞬移
                            {
                                dp[v][j][L - w] = dp[pos][j + 1][k];
                                if (!vis[v])
                                    q.push(v), vis[v] = 1;
                            }
                        }
                    }
                }
            }
        }
    }
    int ans = 0x3f3f3f3f;
    for (int j = 0; j <= K; j++)     //剩余j次瞬移机会
        for (int k = 0; k <= L; k++) //剩下k单位瞬移
            ans = min(ans, dp[1][j][k]);
    cout << ans << endl;
}
void solve()
{
    cin >> A >> B >> M >> L >> K;
    for (int i = 1; i <= A + B; i++)
        mp[i].clear();
    for (int i = 1; i <= M; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        mp[u].emplace_back(make_pair(v, w));
        mp[v].emplace_back(make_pair(u, w));
    }
    memset(dp, 0x3f3f3f3f, sizeof dp);
    memset(vis, 0, sizeof vis);
    bfs(A + B);
}
int main()
{
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值