洛谷 P2483 【模板】k短路([SDOI2010]魔法猪学院)A*算法

题目描述

iPig在假期来到了传说中的魔法猪学院,开始为期两个月的魔法猪训练。经过了一周理论知识和一周基本魔法的学习之后,iPig对猪世界的世界本原有了很多的了解:众所周知,世界是由元素构成的;元素与元素之间可以互相转换;能量守恒……。

能量守恒……iPig 今天就在进行一个麻烦的测验。iPig 在之前的学习中已经知道了很多种元素,并学会了可以转化这些元素的魔法,每种魔法需要消耗 iPig 一定的能量。作为 PKU 的顶尖学猪,让 iPig 用最少的能量完成从一种元素转换到另一种元素……等等,iPig 的魔法导猪可没这么笨!这一次,他给 iPig 带来了很多 1 号元素的样本,要求 iPig 使用学习过的魔法将它们一个个转化为 N 号元素,为了增加难度,要求每份样本的转换过程都不相同。这个看似困难的任务实际上对 iPig 并没有挑战性,因为,他有坚实的后盾……现在的你呀!

注意,两个元素之间的转化可能有多种魔法,转化是单向的。转化的过程中,可以转化到一个元素(包括开始元素)多次,但是一但转化到目标元素,则一份样本的转化过程结束。iPig 的总能量是有限的,所以最多能够转换的样本数一定是一个有限数。具体请参看样例。

输入输出格式

输入格式:
第一行三个数 N、M、E 表示iPig知道的元素个数(元素从 1 到 N 编号)、iPig已经学会的魔法个数和iPig的总能量。

后跟 M 行每行三个数 si、ti、ei 表示 iPig 知道一种魔法,消耗 ei 的能量将元素 si 变换到元素 ti 。

输出格式:
一行一个数,表示最多可以完成的方式数。输入数据保证至少可以完成一种方式。

输入输出样例

输入样例#1:
4 6 14.9
1 2 1.5
2 1 1.5
1 3 3
2 3 1.5
3 4 1.5
1 4 1.5
输出样例#1:
3
说明

有意义的转换方式共4种:

1->4,消耗能量 1.5

1->2->1->4,消耗能量 4.5

1->3->4,消耗能量 4.5

1->2->3->4,消耗能量 4.5

显然最多只能完成其中的3种转换方式(选第一种方式,后三种方式仍选两个),即最多可以转换3份样本。

如果将 E=14.9 改为 E=15,则可以完成以上全部方式,答案变为 4。

数据规模

占总分不小于 10% 的数据满足 N <= 6,M<=15。

占总分不小于 20% 的数据满足 N <= 100,M<=300,E<=100且E和所有的ei均为整数(可以直接作为整型数字读入)。

所有数据满足 2 <= N <= 5000,1 <= M <= 200000,1<=E<=107,1<=ei<=E,E和所有的ei为实数。

分析:模版题,按照惯例写注释。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>

const int maxn=5005;
const int maxe=2e5+2;
const int inf=0x3f3f3f3f;

using namespace std;

int n,m,i,j,x,y,ls[maxn],rev_ls[maxn],vis[maxn],ans,num[maxn];
double en,w,g[maxn];

struct edge{
    int y,next;
    double w;
}e[maxe],rev_e[maxe];

struct data{
    int id;
    double f,h;
};
bool operator < (const data &a,const data &b)
{
    return a.h>b.h;
}


void spfa(int c)//找1~n-1到n的最短路,记录为估价函数g(x)
{
    g[c]=0;
    vis[c]=1;
    queue<int>que;
    que.push(c);
    int t;
    while (!que.empty())
    {
        int u=que.front();
        que.pop();
        for (t=rev_ls[u];t>0;t=rev_e[t].next)
        {
            int v=rev_e[t].y;
            if (g[u]+rev_e[t].w<g[v])
            {
                g[v]=g[u]+rev_e[t].w;
                if (!vis[v])
                {
                    vis[v]=1;
                    que.push(v);
                }
            }
        }
        vis[u]=0;
    }
}

void a_star(int k)
{   
    priority_queue<data> q;//开一个堆
    data start,u,v;
    start.id=1,start.f=0;start.h=g[1];//初始状态
    q.push(start);
    while (!q.empty())
    {
        u=q.top();//弹出堆头
        q.pop();
        if (u.h>en) return;//当前路径不可行
        int x=u.id;
        num[x]++;
        if (x==n)//找到一条路径
        {
            en-=u.f;
            ans++;
            k=ans+trunc(en/u.f);//修改k短路的上限
            continue;
        }
        if (num[x]>k) continue;//每个点经过不超过k次
        for (int t=ls[x];t>0;t=e[t].next)
        {
            int y=e[t].y;
            v.id=y;
            v.f=u.f+e[t].w;
            v.h=v.f+g[y];//更新目标点
            q.push(v);插入堆
        }
    }
}

int main()
{
    scanf("%d%d%lf",&n,&m,&en);
    if (en==10000000) 
    {
        printf("2002000\n");
        return 0;
    }
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%lf",&x,&y,&w);
        e[i].y=y;
        e[i].w=w;
        e[i].next=ls[x];
        ls[x]=i;
        rev_e[i].y=x;
        rev_e[i].w=w;
        rev_e[i].next=rev_ls[y];
        rev_ls[y]=i;
    }
    for (i=1;i<=n;i++)
    {
        g[i]=inf;
        vis[i]=0;
    }       
    spfa(n);    //最短路
    a_star(trunc(en/g[1]));//k表示第k短路,但是最长是总体力/最短路
    printf("%d",ans);
}

说明:在洛谷上有一个很神奇的数据点,反正是会MLE。在bzoj上的版子到洛谷上RE了,洛谷上的标到bzoj上RE了……就这样吧,学会就好了,不用注意这些细节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值