【问题标题】:How exactly can I handle the following condition while applying dijkstra's algorithm to this?在将dijkstra算法应用于此时,我该如何处理以下情况?
【发布时间】:2015-11-02 17:38:22
【问题描述】:

所以,我正在解决以下问题:http://www.spoj.com/problems/ROADS/en/

以数字 1 ... N 命名的 N 个城市与单向道路相连。每条道路都有两个与之相关的参数:道路长度和道路需要支付的通行费(以硬币数量表示)。鲍勃和爱丽丝曾经住在城市 1。在注意到爱丽丝在他们喜欢玩的纸牌游戏中作弊后,鲍勃与她分手并决定搬走 - 到城市 N。他想尽快到达那里可能,但他缺钱。我们想帮助 Bob 找到他有钱可以负担的从城市 1 到城市 N 的最短路径。

输入

输入以测试用例的数量 t 开始。然后是 t 个测试用例。每个测试用例的第一行包含整数 K,0

输出

对于每个测试用例,输出一行包含从城市 1 到城市 N 的最短路径的总长度,其总通行费小于或等于 K 个硬币。如果该路径不存在,则输出-1。

现在,我所做的是,我尝试使用 djikstra 的算法,如下所示:

我不是只有一个节点作为状态,而是 节点和硬币作为一种状态,然后应用dijkstra。 长度是状态之间的权重。 我在不超过总硬币的情况下最小化长度。

我的代码如下:

using namespace std;
#define ll long long
#define pb push_back
#define mp make_pair

class node
{
public:
    int vertex;
    int roadlength;
    int toll;
};

int dist[101][101]; // for storing roadlength
bool visited[101][10001];
int cost[101][101]; // for storing cost
int ans[101][10001]; // actual distance being stored here
void djikstra(int totalcoins, int n);

bool operator < (node a, node b)
{
    if (a.roadlength != b.roadlength)
        return a.roadlength < b.roadlength;
    else if (a.toll != b.toll)
        return a.toll < b.toll;
    return a.vertex < b.vertex;
}

int main (void)
{
    int a,b,c,d;
    int r,t,k,n,i,j;
    cin>>t;
    while (t != 0)
    {
        cin>>k>>n>>r;
        for (i = 1; i <= 101; i++)
            for (j = 1; j <= 101; j++)
                dist[i][j] = INT_MAX;

        for (i = 0; i <= n; i++)
            for (j = 0; j <= k; j++)
                ans[i][j] = INT_MAX;

        for ( i = 0; i <= n; i++ )
            for (j = 0; j <= k; j++ )
                visited[i][j] = false;

        for (i = 0; i < r; i++)
        {
            cin>>a>>b>>c>>d;
            if (a != b)
            {
                dist[a][b] = c;
                cost[a][b] = d;
            }
        }
        djikstra(k,n);
        int minlength = INT_MAX;
        for (i = 1; i <= k; i++)
        {
            if (ans[n][i] < minlength)
                minlength = ans[n][i];
        }
        if (minlength == INT_MAX)
            cout<<"-1\n";
        else
            cout<<minlength<<"\n";
        t--;
    }
    cout<<"\n";
    return 0;
}

void djikstra(int totalcoins, int n)
{
    set<node> myset;
    myset.insert((node){1,0,0});
    ans[1][0] = 0;
    while (!myset.empty())
    {
        auto it = myset.begin();
        myset.erase(it);
        int curvertex = it->vertex;
        int a = it->roadlength;
        int b = it->toll;
        if (visited[curvertex][b] == true)
            continue;
        else
        {
            visited[curvertex][b] = true;
            for (int i = 1; i <= n; i++)
            {
                if (dist[curvertex][i] != INT_MAX)
                {
                    int foo = b + cost[curvertex][i];
                    if (foo <= totalcoins)
                    {
                        if (ans[i][foo] >= ans[curvertex][b] + cost[curvertex][i])
                        {
                            ans[i][foo] = ans[curvertex][b] + cost[curvertex][i];
                            myset.insert((node){i,ans[i][foo],foo});
                        }
                    }
                }
            }
        }
    }
}

现在,我有两个疑问:

  1. 首先,对于问题的第一个给定测试用例,我的输出不正确,即

    Sample Input:

    2
    5
    6
    7
    1 2 2 3
    2 4 3 3
    3 4 2 4
    1 3 4 1
    4 6 2 1
    3 5 2 0
    5 4 3 2
    0
    4
    4
    1 4 5 2
    1 2 1 0
    2 3 1 1
    3 4 1 0
    
    Sample Output:
    11
    -1
    

我的输出结果是4 -1,这对于第一个测试用例来说是错误的。我哪里错了?

  1. 如何处理具有多个边缘的情况?也就是问题提到,Notice that different roads may have the same source and destination cities.这种情况怎么处理?

【问题讨论】:

  • 您在使用调试器单步执行代码时观察到了什么?
  • 这不是和你刚才问的一样的数学题(只是不同的单词问题和不同的输入格式)吗?
  • @JSF,是的,因此我使用了相同的方法,但是我得到了一个 WA,我不知道为什么,而且我无法处理拥有的特定条件多个边缘。连这样一个简单的问题我都做不了,我感到很遗憾。 :(
  • @hans 首先,对于问题的第一个给定测试用例,我的输出不正确 -- 我哪里出错了? -- 嗯...为什么不调试您的代码?输出不正确并修复它是学习如何编写程序的一部分。
  • 看起来您在代码的不同部分以不同的方式使用您的 ans 数组,很不清楚您希望 ans 的索引意味着什么。即对于这条线ans[i][foo] = ans[curvertex][b] + cost[curvertex][i];,看起来您希望它是一个成本,其中 i 是您的目的地,而 foo 是您的成本?这没有多大意义。但是在这里minlength = ans[n][i]; 您似乎希望它输出最小长度?我认为如果您解决此问题,您的代码中的主要问题将得到解决

标签: c++ algorithm dijkstra


【解决方案1】:

存储道路的简单方法是作为向量的向量。对于每个起点城市,您都希望拥有从该城市通往的所有道路的矢量。

因此,当您处理已发现的通往某个城市的“最佳”路径时,您将遍历该城市的所有道路,以查看它们是否可能是通往其他城市的“最佳”路径。

和以前一样,您有两个相互影响的“最佳”定义,而不是简单地组合成一个定义。最短更重要,因此“最佳”的主要定义是最短的,仅在平局的情况下才考虑最便宜。但是您还需要仅考虑最便宜的“最佳”的替代定义。

正如我针对另一个问题所建议的那样,您可以按照“最佳”的主要定义进行排序,这样您就始终先处理该定义中较好的路径,然后再处理较差的路径。然后,您需要针对“最佳”的第二个定义跟踪迄今为止所见的最佳,以便您仅在第二个定义中没有更好的路径时从处理中修剪路径,而这些路径在您已经处理的第一个定义优先级。

【讨论】:

  • 我也想过,但是如果我使用vector&lt;vector&lt;pair&lt;int,int&gt;&gt; &gt;,我将只能存储距离/通行费和目标顶点之一。
  • 如果您不知道如何定义和使用structs,是时候学习了。 std 库确实具有足够的灵活性,即使不定义自己的 struct,也有许多不同的方法来保存这些数据,但您应该学习如何定义自己的结构。
  • 我知道如何定义struct,但我觉得class和struct可以互换使用。此外,您通过第一佳和第二佳概念提出的解决方案,我觉得我已经作为我定义的运营商的一部分这样做了。
  • 当你明白你需要一个包含三个值的向量的向量时,你为什么要谈论vector&lt;vector&lt;pair&gt;&gt;&gt;?只需定义一个结构来保存三个值并创建该结构的向量向量。
  • 这样,我可以通过从产生最小值的特定顶点遍历所有可能的道路来处理多条边,对吧?
【解决方案2】:

我没有读过你的代码,但是我可以告诉你这个问题不能用未经修改的 Dijkstra 算法解决。

这个问题至少和二进制 knapsack problem 一样难。如何?这个想法是在所述问题中构建背包问题。由于已知背包问题在多项式时间内无法解决,因此上述问题也不是。由于 Dijkstra 算法是多项式算法,因此不能适用。

考虑一个具有一组D 多个值X 和一个最大值m = max(X) 的二元背包问题。现在构建提出的问题:

假设有D + 1 城市n 通过两条道路与城市n + 1 相连。让城市1D 唯一对应X 中的一个值v。让从这样一个城市n 出发的两条道路只通往城市n + 1,一条成本为v,距离为m - v + 1,另一条成本为0,距离为m + 1

从本质上讲,“一分钱一分货”——每花一分钱,您的行程就会缩短一个单位。

这将问题重新定义为“鲍勃只在每次通行费上不花钱或只花钱一次,最多可以花多少钱?”这和我们一开始的二元背包问题是一样的。

因此,如果我们解决了所述问题,我们也可以解决二元背包问题,因此所述问题不能比二元背包问题更“有效”——使用 Dijkstra 算法。

【讨论】:

  • 我认为你错了。背包问题试图在有限制的情况下最大化。这个问题不会试图最大化。它实际上不是 Dijkstra 的算法。但它在代码上与 Dijkstra 的算法极其相似,并且可以比背包问题快得多。
  • 我的减少最大化了 Bob 的钱包所花费的金额,同时相应地最小化了距离。实际上满足背包问题要求的是在减少限制下的成本最大化。
  • 现在您添加了“未修改”,我对您的主张的大部分反对意见都已消失。发布的代码是 Dijkstra 算法的修改版本,该算法存在缺陷,导致它无缘无故变慢。但即使修复了这个缺陷,这个“修改后的 Dijkstra”也必然比 Dijkstra 慢。对于“合理”的输入,它可能会稍微慢一些。但我认为你可能是正确的,对于病理输入,它变得病理性变慢。它当然缺乏 Dijkstra 算法的所有性能保证。
猜你喜欢
  • 2012-12-16
  • 2021-01-31
  • 1970-01-01
  • 2011-01-02
  • 1970-01-01
  • 2021-08-02
  • 1970-01-01
  • 2017-04-30
  • 2016-09-12
相关资源
最近更新 更多