• 分层最短路


    洛谷 飞行路线

    题目描述
    Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在nn个城市设有业务,设这些城市分别标记为00到n-1n−1,一共有mm种航线,每种航线连接两个城市,并且航线有一定的价格。
    Alice和Bob现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多kk种航线上搭乘飞机。那么Alice和Bob这次出行最少花费多少?
    输入格式
    数据的第一行有三个整数,n,m,kn,m,k,分别表示城市数,航线数和免费乘坐次数。
    第二行有两个整数,s,ts,t,分别表示他们出行的起点城市编号和终点城市编号。
    接下来有m行,每行三个整数,a,b,ca,b,c,表示存在一种航线,能从城市aa到达城市bb,或从城市bb到达城市aa,价格为cc。
    输出格式
    只有一行,包含一个整数,为最少花费。
    输入输出样例
    输入 #1 复制
    5 6 1
    0 4
    0 1 5
    1 2 5
    2 3 5
    3 4 5
    2 3 3
    0 2 100
    输出 #1 复制
    8
    说明/提示
    对于30%的数据,2 le n le 50,1 le m le 300,k=02≤n≤50,1≤m≤300,k=0;
    对于50%的数据,2 le n le 600,1 le m le 6000,0 le k le 12≤n≤600,1≤m≤6000,0≤k≤1;
    对于100%的数据,2 le n le 10000,1 le m le 50000,0 le k le 102≤n≤10000,1≤m≤50000,0≤k≤10,0 le s,t<n,0 le a,b<n,a eq b,0 le c le 10000≤s,t<n,0≤a,b<n,a≠b,0≤c≤1000
    2018.12.10 增加一组 hack 数据

    说什么分层最短路,我是做到这种类型的了,然后一蒙,,什么啊,但是搜出相关题目一看,这个做法还是很好理解的,就是有点dp的思想
    明显,此题为最短路问题,但是考虑到可以免费搭乘(即直接通过一条边无需费用.)

    这种问题有一个官方的名字 分层图最短路问题

    分层图最短路是指在可以进行分层图的图上解决最短路问题.

    是不是听起来就很nb?

    具体分层图是啥,我也不知道

    一般模型:

    ​  在图上,有kk次机会可以直接通过一条边,问起点与终点之间的最短路径.

    很明显,这道题是一个裸的分层图最短路问题 (貌似这类问题都挺裸的 emm

    解法 我们设

    dis[i][j]代表到达ii用了jj次免费机会的最小花费.
    vis[i][j]代表到达ii用了jj次免费机会的情况是否出现过.

    对于某条路径我们可以选择使用机会,也可以选择不使用机会.

    讨论这两种情况即可

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    using namespace std;
    int n , m , k , s , t ;
    const int N = 1e6 + 10 ;
    int e[N] , h[N] , w[N] , ne[N] , idx , vis[20100][15] , dis[20100][15];
    void add(int a , int b , int c)
    {
    	e[++ idx] = b , w[idx] = c , ne[idx] = h[a] , h[a] = idx ;
    }
    struct node
    {
    	int u , d , used ;
    	bool operator< (const node& a) const 
    	{
    		return d > a.d ;
    	}
    };
    void dijkstra()
    {
    	
    	priority_queue<node> q ;
    	memset(dis , 0x7f , sizeof dis) ;
    	dis[s][0] = 0 ;
    	q.push(node{s , 0 , 0}) ;
    	while(q.size())
    	{
    		int u = q.top().u , now = q.top().used ;
    		q.pop() ;
    		if(vis[u][now]) continue ;
    		vis[u][now] = true ;
    		for(int i = h[u] ;i != -1 ;i = ne[i])
    		{
    			if(now < k && !vis[e[i]][now + 1] && dis[e[i]][now + 1] > dis[u][now])
    			 dis[e[i]][now + 1] = dis[u][now] , q.push(node{e[i] , dis[e[i]][now + 1] , now + 1}) ;
    			if(!vis[e[i]][now] && dis[e[i]][now] > dis[u][now] + w[i])
    			 dis[e[i]][now] = dis[u][now] + w[i]  , q.push(node{e[i] , dis[e[i]][now] , now}) ;
     		}
    	}
    
    }
    int main()
    {
    	memset(h , -1 , sizeof h) ;
    	cin >> n >> m >> k >> s >> t ;
    	for(int i = 1;i <= m;i ++)
    	 {
    	 	int a , b , c ;
    	 	cin >> a >> b >> c ;
    	 	add(a , b , c) ;
    	 	add(b , a , c) ;
    	 }
    	dijkstra() ;
    	int ans = 0x7f7f7ff ;
    	for(int i = 0;i <= k;i ++)
    	 ans = min(ans , dis[t][i]) ;
    	cout << ans << endl ;
    	return 0 ;
    } 
    

    Joyride
    这是七月的另一个美好的阳光灿烂的日子 - 你决定和你的小女儿Joy一起度过你的一天。因为她真的很喜欢下一个小镇的仙女公园,所以你决定去那里度过一天。你的妻子(不幸的是她必须工作)同意带你去公园并再次接你。唉,她非常挑剔准时,所以她告诉你她何时会在公园的正门接你,你必须在那个时候到那儿。你显然也不想在外面等,因为这会让你的小女儿感到难过 - 她本可以在公园里度过更多时间!
    现在你必须计划你在公园的住宿。你知道什么时候到达,什么时候你必须离开。该公园由几个游乐设施组成,由小型人行道相连。进入公园是免费的,但您必须支付公园每次骑行的每次使用费。由于它是Joy
    最喜欢的公园,您已经知道每次乘坐需要多长时间以及每次乘车的费用。
    在公园散步的时候,你沿着它走路时显然不能跳过(即使Joy已经使用过它),否则Joy会很伤心。由于Joy非常喜欢这个公园,她很乐意不止一次使用游乐设施。在两个游乐设施之间行走需要一定的时间。
    由于您是公积金的父母,您希望在公园时尽可能少花钱。
    你能计算出绝对必要的数量吗?

    输入
    输入包括:
    •一行,整数x(1≤x≤1000)表示您到达之间的时间和您将被接收的时间(以分钟为单位);
    •一行有三个整数n,m和t,其中

    • n(1≤n≤1000)是公园内的游乐设施数量;
    • m(1≤m≤1000)是路面的数量;
    • t(1≤t≤1000)是从一辆车到另一辆车的人行道所需的分钟数。
      •m行,每行包含两个整数a和b(1≤a,b≤n),表示在游乐设施a和b之间有一个路面。
      •n行各含两个整数t和p(1≤T,P≤10 6),指出对应的乘坐需要吨分钟,费用p欧元。
      您总是从1号车开始,并且在入住结束时必须返回1号车,因为入口位于那里。这意味着你必须至少两次使用1次(一次进入,一次出口)。如果你已经到达,你可以多次乘坐。

    输出
    输出一行包含一个整数,在公园停留x分钟所需的最小量,或者它是一个陷阱。(包括期间)如果不能保持正好x分钟。
    样例输入
    复制样例数据
    4
    4 4 1
    1 2
    2 3
    3 4
    4 1
    1 2
    2 1
    5 4
    3 3
    样例输出
    8

    多开一维状态记录时间,d[i][t] = 经过时间t走到节点i的最小花费
    每一个状态分别向“原地等待”与“前往下一个节点”转移

    #include <iostream>
    #include <queue>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int  N = 1e4 + 10 ;
    typedef long long ll ;
    int h[N * 2] , vis[N][1005] ;
    ll dis[N][1005];
    ll p[N] , t[N] ;
    int n , m , pick  , T ;
    struct EDGE
    {
        int v , ne;
        ll cost ; 
    } edge[N * 2];
    struct node
    {
        int u ;
        ll  d , k ; // 结点  花费   时间 
        bool operator<(const node& n) const
        {
            return d > n.d ;
        } 
    };
    int idx ;
    void add(int a , int b , ll  c)
    {
        edge[++ idx].v = b , edge[idx].cost = c , edge[idx].ne = h[a] , h[a] = idx ;
    }
     
    void dijkstra()
    {
        priority_queue<node> q; 
         memset(dis , 0x3f , sizeof dis) ;
         memset(vis , 0 , sizeof vis) ;
         q.push(node{1 , p[1] , t[1]});
         dis[1][t[1]] = p[1] ;
         while(q.size())
         {
            int u = q.top().u ;
            ll k = q.top().k ;
            q.pop() ;
            if(vis[u][k]) continue ;
            vis[u][k] = 1 ;
            // 这个地方不是很懂,应该是dis[u][k + t[u] = min(dis[u][k + t[u] , dis[u][[k] + p[u])
            // 就相当在u点等待花费时间k + t[u] 的花费钱数,然后就相当于到达u点花费k时间,然后又加上了一个u点的花费
            if(k + t[u] <= pick && dis[u][k + t[u]] > dis[u][k] + p[u])
             dis[u][k + t[u]] = dis[u][k] + p[u] , q.push(node{u , dis[u][k + t[u]] , k + t[u]} ) ;
            for(int i = h[u] ;i != -1 ;i = edge[i].ne)
            {
            ll w = edge[i].cost , v = edge[i].v ;
                if(k + w + t[v] <= pick && dis[v][k + w + t[v]] > dis[u][k] + p[v])
                 dis[v][k + w + t[v]] = dis[u][k] + p[v], q.push(node{v , dis[v][k + w +  t[v]] , k +w +  t[v]} ) ;
            }
         }
    }
    
    int main()
    {
        memset(h , -1 , sizeof h) ;
        scanf("%d%d%d%d",&pick , &n , &m , &T) ;
        for(int i = 1;i <= m;i ++)
        {
            int a , b ;
            cin >> a >> b ;
            add(a , b , T) , add(b , a , T) ; 
        }
        for(int i = 1;i <= n; i ++)
         cin >> t[i] >> p[i] ;
        dijkstra() ;
        if(vis[1][pick])
         cout << dis[1][pick] << endl ;
        else
         cout << "It is a trap." << endl ;
        return 0 ;
    }
    
    每次做题提醒自己:题目到底有没有读懂,有没有分析彻底、算法够不够贪心、暴力够不够优雅。
  • 相关阅读:
    [Bzoj1009][HNOI2008]GT考试(动态规划)
    [Bzoj1016][JSOI2008]最小生成树计数
    [Bzoj3224][Tyvj1728] 普通平衡树(splay/无旋Treap)
    [Bzoj1061][Noi2008]志愿者招募(费用流)
    [Bzoj1087][SCOI2005]互不侵犯King(状压dp)
    [Bzoj1051][HAOI2006]受欢迎的牛(tarjan)
    [Bzoj1003][ZJOI2006]物流运输(spfa+dp)
    [Bzoj1026][SCOI2009]windy数(数位dp)
    插入随机数到MySQL数据库
    SSH框架整合配置所需JAR包(SSH整合)
  • 原文地址:https://www.cnblogs.com/spnooyseed/p/12870923.html
Copyright © 2020-2023  润新知