hrbust 1404 Leyni的汽车比赛【dp+思维优化】好题!

351 篇文章 2 订阅

Leyni的汽车比赛
Time Limit: 1000 MSMemory Limit: 65536 K
Total Submit: 42(7 users)Total Accepted: 13(6 users)Rating: Special Judge: No
Description

Leyni参加了一场汽车比赛,这个比赛是在n个城市中进行的,城市的编号为1..n,而且任意两个城市之间都存在一条双向的路。官方要Leyni进行r回合比赛,在第i回合中,官方要求Leyni从城市si出发并在城市ti结束,并允许他在任何一个城市更换汽车,但是最多允许更换ki次。

官方为Leyni准备了m种汽车,这些汽车可能通过不同的道路不同的方向时需要不同的时间。

请帮助Leyni计算对应每一回合,他最少需要多久。


Input

输入包含多组测试数据。

对于每组测试数据:

1行,包含三个整数n, m, r (2 ≤ n ≤ 50, 1 ≤ m ≤ 50, 1 ≤ r ≤ 105)

接下来包含mn * n的矩阵,每个元素是在[0, 106]内整数。

i个矩阵中第j行第k列表示第i种汽车通过城市j向城市k的道路所需要的时间,输入数据保证对角线都是0

接下来r行中,每行包含三个整数si, ti, ki (1 ≤ si, ti ≤ n, si != ti, 0 ≤ ki ≤ 1000),表示每回合的起点,终点,更换汽车次数的限制。

 

处理到文件结束


Output

对于每组测试数据:

请对应每回合输出完成该回合所需要的最少时间。


Sample Input

4 2 3

0 1 5 6

2 0 3 6

1 3 0 1

6 6 7 0

0 3 5 6

2 0 1 6

1 3 0 2

6 6 7 0

1 4 2

1 4 1

1 4 3


Sample Output

3

4

3

Source
Harbin University of Science and Technology Collegiate Programming Contest Spring 2012 - Practice
Author
齐达拉图@HRBUST

思路:


1、考虑dp,不难想到其需要转移的状态都有:

①从哪儿,到哪儿。

②此时在用哪辆车,上一次用的哪辆车。

③此时一共交换了多少次车。

那么就有:dp【i】【j】【k】【l】表示从i到j,已经交换了k次车,此时的车号为l的最小花费。


2、设定好了状态之后,那么考虑写状态转移方程;

①dp【i】【j】【k】【l】=min(dp【i】【j】【k】【l】,dp【i】【u】【k-1】【z】+map【l】【u】【j】);

其中三层for用来写Floyd,一层for用来枚举k,一层for枚举l,当然还要一层for枚举z;那么此时时间复杂度是:O(N^3M^2k);

②我们首先考虑这样一个问题,对应一个图最多只有50个点,那么其交换的车的次数是不需要1000次那么多的,每次都到达一个点,我们没比要到达一个点之后回头走之前走过的点,那么第一个简答优化,如果对应ki>n,那么其实多余出来的交换次数直接忽略即可,那么最多交换次数相当于n次,那么此时时间复杂度优化至O(N^4M^2)。

③但是显然此时的时间复杂度还是非常高的,需要继续优化。

我们dp的是当前从i到j使用车号为l的情况,那么我们其实上一次车号的情况是无所谓的,那么此时可以继续优化:

同时降低一个维度,设定dp【i】【j】【k】表示第i次交换车子,从j到k的最小花费。

那么我们可以写状态转移方程为:

dp【i】【k】【l】=min(dp【i-1】【k】【j】+map【z】【j】【l】,dp【i】【k】【l】);

三层for枚举点进行Floyd,一层for枚举i,一层for枚举z,那么此时时间复杂度降低至:O(N^4M);但是时间复杂度依旧很高,考虑继续优化。(其实我优化到这里已经想了很久了,是想拼一发,写了提交还是TLE,所以继续考虑优化)

④那么我们考虑这个问题,三层for写的Floyd是一定不能动的,一层for枚举i也是一定不能动的,那么我们考虑如何优化枚举z这一块。

⑤我们枚举z,显然是为了让dp【i-1】【k】【j】+map【z】【j】【l】维护找到最小值,那么我们可以优化,其实:min(map【z】【j】【l】)==dp【0】【j】【l】;dp【0】【j】【l】表示的是从j到l不进行交换车子的最小花费,那么我们上式中枚举的map【z】【j】【l】想要达到的目的就是找到最小的map【z】【j】【l】;而且dp【0】【j】【l】表示的就是这个最小值,那么此时状态转移方程为:

dp【i】【k】【l】=min(dp【i-1】【k】【j】+dp【0】【j】【l】,dp【i】【k】【l】);使得不需要在枚举z,时间复杂度优化至:O(n^4);此时的时间复杂度就是一个可行的时间复杂度。


3、思路最终构建完毕,剩下的内容就是写代码啦~


Ac代码:


#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int n,m,q;
int map[55][55][55];
int dp[55][55][55];
int main()
{
    while(~scanf("%d%d%d",&n,&m,&q))
    {
        memset(dp,0x3f3f3f3f,sizeof(dp));
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                for(int k=0;k<n;k++)
                {
                    scanf("%d",&map[i][j][k]);
                }
            }
        }
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                for(int k=0;k<n;k++)
                {
                    for(int l=0;l<n;l++)
                    {
                        map[i][k][l]=min(map[i][k][l],map[i][k][j]+map[i][j][l]);
                    }
                }
            }
            for(int j=0;j<n;j++)
            {
                for(int k=0;k<n;k++)
                {
                    dp[0][j][k]=min(dp[0][j][k],map[i][j][k]);
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<n;j++)
            {
                for(int k=0;k<n;k++)
                {
                    for(int l=0;l<n;l++)
                    {
                        if(dp[i-1][k][j]==0||map[0][j][l]==0)continue;
                        dp[i][k][l]=min(dp[i-1][k][j]+dp[0][j][l],dp[i][k][l]);
                    }
                }
            }
        }
        while(q--)
        {
            int output=0x3f3f3f3f;
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(z>=n)z=n;
            x--;y--;
            for(int i=0;i<=z;i++)
            {
                output=min(output,dp[i][x][y]);
            }
            printf("%d\n",output);
        }
    }
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值