NOI2014 魔法森林 day1t2 SPFA

这道题做法还是很多的,至少有人用最优性剪枝当场A掉了。我只有50分的暴力分(写丑了),SB我不会LCT,先写一下SPFA的神思路做法(说白了还是我SB),LCT以后定补。ORZ想出来的人(hq大神)。最近总感觉考试时没有发挥出全力,考完后一想才发现做法其实还是很好想的。

题意:给一个节点n边m的图,每条边有两个权值a和b,要从1选择两个数x,y出发通过\(a \le x,\ \ b \le y\)的边走到n,求最小的x+y。

想想What's SPFA?最短路?但是我们可以利用松弛操作找到特点两点间的边的最大值。用dis数组来保存从1点到当前点的路径上最大的b称为当前点花费b。做法的精妙之处在于:我们可以从小到大枚举x,之前能到达的点,现在仍然能到达,所以不用管x;如果SPFA时不清空dis数组,里面保存的就是在之前的较小的x的情况下能走到的点的花费b,现在有可能会通过走新边来减小b,新边就是权值恰好为a的边,每次取有这样的边的点进行SPFA即可。

真是一道巧妙的题!

  1 //{HEADS
  2 #define FILE_IN_OUT
  3 #define debug
  4 #include <cstdio>
  5 #include <cstring>
  6 #include <cstdlib>
  7 #include <cmath>
  8 #include <ctime>
  9 #include <algorithm>
 10 #include <iostream>
 11 #include <fstream>
 12 #include <vector>
 13 #include <stack>
 14 #include <queue>
 15 #include <deque>
 16 #include <map>
 17 #include <set>
 18 #include <bitset>
 19 #include <complex>
 20 #include <string>
 21 #define REP(i, j) for (int i = 1; i <= j; ++i)
 22 #define REPI(i, j, k) for (int i = j; i <= k; ++i)
 23 #define REPD(i, j) for (int i = j; 0 < i; --i)
 24 #define STLR(i, con) for (int i = 0, sz = con.size(); i < sz; ++i)
 25 #define STLRD(i, con) for (int i = con.size() - 1; 0 <= i; --i)
 26 #define CLR(s) memset(s, 0, sizeof s)
 27 #define SET(s, v) memset(s, v, sizeof s)
 28 #define mp make_pair
 29 #define pb push_back
 30 #define PL(k, n) for (int i = 1; i <= n; ++i) { cout << k[i] << ' '; } cout << endl
 31 #define PS(k) STLR(i, k) { cout << k[i] << ' '; } cout << endl
 32 using namespace std;
 33 #ifdef debug
 34 #ifndef ONLINE_JUDGE
 35     const int OUT_PUT_DEBUG_INFO = 1;
 36 #endif
 37 #endif
 38 #ifdef ONLINE_JUDGE
 39     const int OUT_PUT_DEBUG_INFO = 0;
 40 #endif
 41 #define DG if(OUT_PUT_DEBUG_INFO)
 42 typedef long long LL;
 43 typedef double DB;
 44 typedef pair<int, int> i_pair;
 45 const int INF = 0x3f3f3f3f;
 46 //}
 47 
 48 const int maxn = 50000 + 10;
 49 const int maxm = 100000 + 10;
 50 const int maxe = maxm * 2;
 51 /*{ 前向星*/
 52 struct Edge {
 53     int edge;
 54     int head[maxn], to[maxe], next[maxe], la[maxe], lb[maxe];
 55     Edge() {
 56         edge = 0;
 57         memset(head, -1, sizeof head);
 58     }
 59     void addedge(int u, int v, int a, int b) {
 60         to[edge] = v;
 61         next[edge] = head[u];
 62         la[edge] = a;
 63         lb[edge] = b;
 64         head[u] = edge++;
 65     }
 66 }E;
 67 /*}*/
 68 int n, m;
 69 
 70 int dis[maxn];
 71 queue<int> Q;
 72 vector<int> p[maxn];
 73 bitset<maxn> vis;
 74 
 75 void spfa(int u, int a) {
 76     Q.push(u);
 77     vis.reset();
 78     vis[u] = 1;
 79     while(!Q.empty()) {
 80         int v = Q.front(); Q.pop();
 81         vis[v] = 0;
 82         for(int i = E.head[v]; i != -1; i = E.next[i]) {
 83             if(E.la[i] <= a && max(dis[v], E.lb[i]) < dis[E.to[i]]) {
 84                 dis[E.to[i]] = max(dis[v], E.lb[i]);
 85                 if(!vis[E.to[i]]) {
 86                     Q.push(E.to[i]);
 87                     vis[E.to[i]] = 1;
 88                 }
 89             }
 90         }
 91     }
 92 }
 93 
 94 int main() {
 95     //FILE_INIT("forest");
 96     freopen("forest.in", "r", stdin);
 97     freopen("forest.out", "w", stdout);
 98 
 99     scanf("%d%d", &n, &m);
100     int Max = -INF;
101     REP(i, m) {
102         int u, v, a, b;
103         scanf("%d%d%d%d", &u, &v, &a, &b);
104         E.addedge(u, v, a, b);
105         E.addedge(v, u, a, b);
106         p[a].pb(u);
107         p[a].pb(v);
108         Max = max(Max, a);
109     }
110     int ans = INF;
111     SET(dis, INF);
112     dis[1] = 0;
113     REP(i, Max) {
114         STLR(j, p[i]) {
115             spfa(p[i][j], i);
116         }
117         if(dis[n] != INF) {
118             ans = min(ans, dis[n] + i);
119         }
120     }
121     if(ans == INF) {
122         printf("%d\n", -1);
123     } else {
124         printf("%d\n", ans);
125     }
126 
127     return 0;
128 }
View Code

 

转载于:https://www.cnblogs.com/hzf-sbit/p/3877739.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值