洛谷 P1073 最优贸易 分层图状态转移 spfa 最长路

题目链接:

https://www.luogu.org/problem/P1073

思想:

1:新的概念:分层图

2:参考博客:https://www.luogu.org/blog/user15019/solution-p1073,特别特别推荐,详细了解分层图的原理就从精读这一个博客开始

思路:

1:读完这道题,可以发现这样的事实:

      1:你可以在图上任意走动

      2:最终答案只与你的买入与卖出价格有关(我们就把买入卖出价值作为边权)

      3:如果你买入了一个水晶球,你是没有不卖它的道理的(显然咯,买了不卖血亏...)

2:分层图可以很好的解决这个问题:由于可以任意走动,所以我们可以建一张图,令图上的边全都是0,表示我的走动对我最终的结果没有影响,考虑某个点 i ,它买入或者卖出水晶球的花费是v[i]

3:那么 当我们进行买入操作,我们就建立一条有向边转移到一张新图上,边的大小为-v[i],指向点i所能到达的点(在第二层图上)而这张新图就是我们的第二层图,它表示:假如我选择走了这条边,就是我在这个点买了这个水晶球,我不会反悔,并且我接下来考虑在某个点卖它

4:当我们进行卖出操作,我们建立一条有向边转移到第三层图上,边的大小为v[i],指向i所能到达的点(在第三层图上),它表示:假如我选择走了这条边,就是我在这个点卖了这个水晶球,我不会反悔,并且我接下来考虑走向终点

5:可以发现,从第一层图走到第二层图走到第三层图走到终点,这就是一个合法的选择,而且分层图把所有可能的决策都考虑到了

6:最后走向终点,我们有两种合法的操作:

     1:不买卖直接走向终点,直接在第一层图的n号节点建立边权为0的有向边接入一个“超级终点”

     2:买卖一次后走向终点,在第三层图的n号节点建立边权为0的有向边接入“超级终点”

7:最后解释一下为什么我们要分层:

      因为当你分了层,你就可以从还未买入这个状态,转移到已经买入准备卖出这个状态,然后再转移到从卖出点走向终点的状态。由于有向边的建立,你不能从第二或三层走回第一层图,这保证了你只做一次买卖,而不是无限做买卖,符合了题目的要求,而我们最终的答案,就是求从第一层图的1号点,经过三层图走到“超级终点”的最长路,

代码:

1:一开始就把边存为负权值,然后跑最短路spfa算法,取负值即为最长路

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e5+1;
int n,m,a,b,c,ing[maxn*3+1],v[maxn],d[maxn*3+1];
vector<pair<int,int> >e[maxn*3+1];

inline void spfa(int s)
{
    memset(ing,0,sizeof(ing));
    memset(d,0x7f,sizeof(d));
    queue<int>q;
    q.push(s);
    d[s]=0;
    ing[s]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        ing[now]=0;
        for(int i=0;i<e[now].size();i++)
        {
            int v=e[now][i].first;
            if(d[v]>d[now]+e[now][i].second)
            {
                d[v]=d[now]+e[now][i].second;
                if(ing[v])
                    continue;
                q.push(v);
                ing[v]=1;
            }
        }
    }
}

inline void addedge(int x,int y)
{
    e[x].push_back(make_pair(y,0));
    e[x+n].push_back(make_pair(y+n,0));
    e[x+2*n].push_back(make_pair(y+2*n,0));
    e[x].push_back(make_pair(y+n,v[x]));
    e[x+n].push_back(make_pair(y+2*n,-v[x]));
}

int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i];
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        addedge(a,b);
        if(c==2)addedge(b,a);
    }
    e[n].push_back(make_pair(3*n+1,0));
    e[n*3].push_back(make_pair(n*3+1,0));
    spfa(1);
    cout<<-d[3*n+1]<<endl;
    return 0;
}

2:求最长路的spfa算法

#include <bits/stdc++.h>

using namespace std;
const int maxn=1e5+1;
int n,m,a,b,c,ing[maxn*3+1],v[maxn],d[maxn*3+1];
vector<pair<int,int> >e[maxn*3+1];

inline void spfa(int s)
{
    memset(ing,0,sizeof(ing));
    memset(d,-0x7f,sizeof(d));
    queue<int>q;
    q.push(s);
    d[s]=0;
    ing[s]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        ing[now]=0;
        for(int i=0;i<e[now].size();i++)
        {
            int v=e[now][i].first;
            if(d[v]<d[now]+e[now][i].second)
            {
                d[v]=d[now]+e[now][i].second;
                if(ing[v])
                    continue;
                q.push(v);
                ing[v]=1;
            }
        }
    }
}

inline void addedge(int x,int y)
{
    e[x].push_back(make_pair(y,0));
    e[x+n].push_back(make_pair(y+n,0));
    e[x+2*n].push_back(make_pair(y+2*n,0));
    e[x].push_back(make_pair(y+n,-v[x]));
    e[x+n].push_back(make_pair(y+2*n,v[x]));
}

int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>v[i];
    for(int i=1;i<=m;i++)
    {
        cin>>a>>b>>c;
        addedge(a,b);
        if(c==2)addedge(b,a);
    }
    e[n].push_back(make_pair(3*n+1,0));
    e[n*3].push_back(make_pair(n*3+1,0));
    spfa(1);
    cout<<d[3*n+1]<<endl;
    return 0;
}
发布了117 篇原创文章 · 获赞 37 · 访问量 6612

猜你喜欢

转载自blog.csdn.net/aiwo1376301646/article/details/100555380