洛谷 5122 (USACO2018Feb 金组 T1 Fine Dining)题解

本文详细介绍了USACO2018February竞赛金组题目Fine Dining的解题思路。首先通过Dijkstra算法计算从n到每个节点的最短路径,接着在保持常数复杂度的情况下,利用“超级起点”解决从草堆节点到其他节点的最短路径问题,以避免暴力求解导致的时间超限。最终通过构建有向边和特定权值来确定经过草堆节点的最短路,并减去美味值进行比较。

题意简述

给定一个无向图(点数n&lt;=5e4n&lt;=5e4n<=5e4,边数m&lt;=1e5m&lt;=1e5m<=1e5),其中有k(k&lt;=n)k(k&lt;=n)k(k<=n)个草堆节点。(kkk行,每行两个数viv_ivi,yiy_iyi表示第iii个草堆在viv_ivi点上,美味值是yiy_iyi)求111n−1n-1n1中有多少节点满足:
经过一个草堆节点到nnn的最短路-这个草堆节点的美味值<=直接到节点nnn的最短路。
输出n−1n-1n1行,第iii个节点要是能满足上述条件,就输出111,否则输出000。(真程序员)

数据

输入
4 5 1
1 4 10
2 1 20
4 2 3
2 3 5
4 3 2
2 7
输出
1
1
1

思路

首先我们要知道nnn到每个节点的最短路吧。。。这个不知道怎么算后面的。。。而且也没有理由不知道,常数不大,数据又小,不会耗费太多时间计算。这样也就算出了nnn到每个草堆节点的距离(每个节点都算了,肯定包含了所有的草堆节点)。(这就告诉我们要先打好Dijkstra的板子)

接下来我们要知道的就是所有草堆节点到所有别的节点的最短路了。有一个很好 暴力的思路,就是每个作起点跑一遍DijkstraDijkstraDijkstra。这样就FFTFFTFFT(即fast−fast−TLEfast-fast-TLEfastfastTLE)了。显然这样是不珂以的。我们有没有什么好一点的办法珂以一遍DijDijDij求出所有草堆的最小值呢?

经过一节生物课,一节政治课,终于,我还是没有思路,只能回来看stdstdstd。正解就是:建一个"超级起点"(网络流题经常这么干,但我怎么也没有想到一个最短路题会这么干),设其编号为n+1n+1n+1,然后让这个点连接所有的草堆节点。那么,这kkk条边中每条边的权值是多少呢?珂以设置成0么?不珂以。。。如果设置成000的话我们只能求出经过某个草垛的最短路,但减去美味值的时候,就不知道减哪个了。。。因为没记录。。。所以考虑这个边权就顺带起记录作用,也就是:
n−1n-1n1连接每一个草堆节点iii,边有向有向有向!边权是dis1[i]−yum[i]dis1[i]-yum[i]dis1[i]yum[i]yum[i]yum[i]yum[i]表示这个点的美味值,dis1[i]dis1[i]dis1[i]表示第一次DijkstraDijkstraDijkstra求出的iiinnn的最短路。

这样就差不多明白怎么写了,其实就稍微比DijkstraDijkstraDijkstra模板代码多一点点而已。代码:

#include<bits/stdc++.h>
#define N 1001000
#define int long long
using namespace std;

class Graph//图
{
    private:
        int head[N];
        int EdgeCount=0;
    public:
        struct Edge
        {
            int To,Label,Next;
        }Ed[N<<1];

        void clear()
        {
            memset(Ed,-1,sizeof(Ed));
            memset(head,-1,sizeof(head));
            EdgeCount=0;
        }
        void AddEdge(int u,int v,int w)
        {
            ++EdgeCount;
            Ed[EdgeCount]=(Edge){v,w,head[u]};
            head[u]=EdgeCount;
        }

        int Start(int i)
        {
            return head[i];
        }

        int To(int i)
        {
            return Ed[i].To;
        }
        int Label(int i)
        {
            return Ed[i].Label;
        }
        int Next(int i)
        {
            return Ed[i].Next;
        }
}G;

struct node
{
    int v,w;
    const bool operator<(const node CompWith) const
    {
        return w>CompWith.w;
    }
};
priority_queue<node>Q;
bool vis[N];
void DijkstraLog(int dis[N],int s)//nlogn的超快Dijkstra
{
    memset(vis,0,sizeof(vis));
    for(int i=0;i<N;++i) dis[i]=0x3f3f3f3f3f3f3f3f;
    Q=priority_queue<node>();

    dis[s]=0;
    Q.push((node){s,0});
    while(!Q.empty())
    {
        node Minnode=Q.top();Q.pop();
        int u=Minnode.v;

        if (vis[u]) continue;
        vis[u]=1;

        for(int i=G.Start(u);~i;i=G.Next(i))
        {
            int v=G.To(i),len=G.Label(i);
            if (!vis[v] and ~v and dis[v]>dis[u]+len)
            {
                dis[v]=dis[u]+len;
                Q.push((node){v,dis[v]});
            }
        }
    }
}

int dis1[N],dis2[N];
int yum[N];
int n,m,k;
void Add(int u,int v,int w,bool directed=0)
{
    G.AddEdge(u,v,w);
    if (!directed)
    {
        G.AddEdge(v,u,w);
    }
}
void Input()
{
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=m;++i)
    {
        int u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        Add(u,v,w);
    }
    for(int i=1;i<=k;++i)
    {
        int pos,val;
        scanf("%lld%lld",&pos,&val);
        yum[pos]=val;
    }
}
void Solve()
{
    DijkstraLog(dis1,n);
    for(int i=1;i<=n;++i)
    {
        if (yum[i])//判断i是否是草堆节点
        {
            Add(n+1,i,dis1[i]-yum[i],1);
        }
    }
    DijkstraLog(dis2,n+1);
    for(int i=1;i<n;i++)
    {
        if (dis1[i]>=dis2[i])//如果满足条件
        {
            puts("1");
        }
        else//不满足条件
        {
            puts("0");
        }
    }
}

main()
{
    G.clear();
    Input();
    Solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值