zoukankan      html  css  js  c++  java
  • 分层图最短路

    分层图最短路,就是在分层图上解决最短路问题
    一般模型为:
    在一张图上,有k次机会可以通过一条边而不需要计算权值(免费过路),求从起点到终点的最短路线
    常规思路:
    想象将一个点拆分为k + 1个点,分别表示到这个点时,免费权消耗了0次,1次,2次......k次
    这样实际我们可以把这k个点想象成对应dp的不同的状态
    dis[i][j]表示到第i个点时,消耗了j次乘车权后的最短路线
    我们用to表示要到达的点,x表示父亲节点,就有
    dis[to][j] = min(dis[x][j] + val(x, to), dis[x][j - 1])
    因为我们跑最短路时是从前向后跑,也就是当前状态推出后继状态,所以实际上我们可以推出两个可能状态
    如果我们消耗了免费过路权
    dis[to][j] = min{dis[x][j - 1]}
    如果我们没消耗免费过路权
    dis[to][j] = min{dis[x][j] + val(x, to)}
    这就提醒我们,我们的队列在加入到达某个点的同时,要分别记录到达这个点时的两种不同的状态,以免造成情况遗漏
    也就是q[i][j]表示到第i个点时,第i个点在j的情况下我们消耗了几次免费过路权,j为0或是1,0表示没有消耗免费过路权,1表示消耗了免费过路权
    到这里我们就能与上面的拆点联系上了,我们想,到了终点时,可能有:用了0次免费过路权,用了1次免费过路权,用了2次,用了3次....用了k次
    也就是k+1种可能状态,此时我们把这k+1种状态,每种状态都想象成原本的这个点拆分出来的一个点,也就相当于这个点拆分出了k+1个点,就和上面接上了
    然后我们合理外推,对于每一个点都可能出现这样的情况,也就相当于每一个点都拆分成了k+1个点,这n*(k+1)个点之间彼此连接,跑最短路,这样可能有点抽象
    实际上把这想象成一个dp的过程是最好理解的

    例题1:move(集训考试题)
    题目描述
    给定一张地图一共有 n 个城市,城市编号为 0 ~ n - 1,这 n 个城市通过 m
    条铁路连接(走一条铁路视为乘车一次)。而小A 想从城市 s 出发,到 达城市 t
    结束。小A 可以免费乘车 k 次,现在他想知道,他这 次旅游的最少花费是多少?
    输入格式
    第一包含三个整数 n, m, k,含义见题目描述。
    第二有两个整数 s, t,表示小A 的出发城市和结束城市。
    接下来 m 行,每行三个整数 x, y, z,表?在城市 x 和 y 之间有一条铁路相
    连,乘车花费为 z。
    输出格式
    输出一行,一个整数表示答案。
    样例输入
    5 6 1
    0 4
    0 1 5
    1 2 5
    2 3 5
    3 4 5
    2 3 3
    0 2 100
    样例输出
    8
    数据范围
    对于 30% 的数据, 2 ≤ n ≤ 50, 1 ≤ m ≤ 300, k = 0;
    对于 50% 的数据, 2 ≤ n ≤ 600, 1 ≤ m ≤ 6000, 0 ≤ k ≤ 1;

    很标准的模板型题目。
    思路就是上面的思路,不加赘述,代码实现口胡不好说,直接读代码吧=-=

      1 #include<iostream>
      2 #include<iomanip>
      3 #include<cstdio>
      4 #include<cstring>
      5 #include<ctime>
      6 #include<cmath>
      7 #include<algorithm>
      8 #include<cstdlib> 
      9 using namespace std;
     10 const int maxn = 100086;
     11 const int inf = 1000000007;
     12 struct node {
     13     int y, net, v;
     14 }e[maxn];
     15 int dis[maxn][11];
     16 int n, m, k, st, ed;
     17 int lin[maxn], len = 0;
     18 int q[maxn][2];//第二维, 0表示当前点没有拆点,1表示当前点进行了拆点,拆成了k+1个点q[i][1]存储的是第i个点拆出的点的编号 
     19 bool vis[maxn][11];
     20 
     21 inline int read() {
     22     int x = 0, y = 1;
     23     char ch = getchar();
     24     while(!isdigit(ch)) {
     25         if(ch == '-') y = -1;
     26         ch = getchar();
     27     }
     28     while(isdigit(ch)) {
     29         x = (x << 1) + (x << 3) + ch - '0';
     30         ch = getchar();
     31     }
     32     return x * y;
     33 }
     34 
     35 inline void insert(int xx, int yy, int vv) {
     36     e[++len].net = lin[xx];
     37     e[len].v = vv;
     38     e[len].y = yy;
     39     lin[xx] = len;
     40 }
     41 
     42 inline void spfa(int st) {
     43     int head = 0, tail = 1;
     44     for(int i = 0; i < n; ++i)
     45         for(int j = 0; j <= k; ++j)
     46             dis[i][j] = inf;
     47     int x, j;//j实际表示使用了多少次免费乘车权,//我们将一个点拆了多次,j同时作为这些被拆出的点的编号 
     48     memset(vis, 0, sizeof(vis));
     49     vis[st][0] = 1, dis[st][0] = 0;
     50     q[1][0] = st, q[1][1] = 0;
     51     while(head != tail) {
     52         head = (head + 1) % 100003;
     53         x = q[head][0];
     54         j = q[head][1];
     55         //vis[x][j] = 1;
     56         for(int i = lin[x]; i; i = e[i].net) {
     57             int to = e[i].y;
     58             if(dis[x][j] + e[i].v < dis[to][j]) {//如果x到to没有使用免费乘车权 
     59                 dis[to][j] = dis[x][j] + e[i].v;
     60                 if(!vis[to][j]) {
     61                     vis[to][j] = 1;
     62                     tail = (tail + 1) % 100003;
     63                     q[tail][0] = to;
     64                     q[tail][1] = j;
     65                 } 
     66             } 
     67             if(j < k && dis[x][j] < dis[to][j + 1]) {//如果x到to使用了免费乘车权 
     68                 dis[to][j + 1] = dis[x][j];
     69                 if(!vis[to][j + 1]) {
     70                     vis[to][j + 1] = 1;
     71                     tail = (tail + 1) % 100003;
     72                     q[tail][0] = to;
     73                     q[tail][1] = j + 1;
     74                 }
     75             }
     76         }
     77         vis[x][j] = 0;
     78     }
     79 }
     80 
     81 int main() {
     82 //    freopen("move.in", "r", stdin);
     83 //    freopen("move.out", "w", stdout);
     84     n = read(), m = read(), k = read();
     85     st = read(), ed = read();
     86     for(int i = 1; i <= m; ++i) {
     87         int x, y, v;
     88         x = read(), y = read(), v = read();
     89         insert(x, y, v);
     90         insert(y, x, v);
     91     }
     92     spfa(st);
     93     int ans = inf;
     94     for(int i = 0; i <= k; ++i)
     95         ans = min(ans, dis[ed][i]);
     96     cout << ans << '
    ';
     97 //    fclose(stdin);
     98 //    fclose(stdout);
     99     return 0;
    100 }
    View Code
  • 相关阅读:
    MFC程序执行过程剖析
    不同位数操作系统的 数据长度
    测试:safenet提供的CheckKey函数 内存泄漏。具体来说是句柄.
    关于更改项目名称
    内存泄漏相关的
    美化MFC 之调整静态文本的颜色 字体。 用于添加公司标题 联系方式 口号等数据
    DAVINCI DM365-DM368开发攻略——开发环境搭建(DVSDK4.02)
    Removing Unnecessary HTTP Headers in IIS and ASP.NET 在ASP.Net和IIS中删除不必要的HTTP响应头
    Implementing Singleton in C#
    WEBAPI VS WCF微软随.NET 4.5发布新REST API框架
  • 原文地址:https://www.cnblogs.com/ywjblog/p/9270423.html
Copyright © 2011-2022 走看看