分层图
分层图学习笔记
分层图的笔记咕了好久,今天(2024/10/15)终于有时间写了。
首先,分层图只是一种思想,而不是一种算法。
从名字上来理解,就是将图论中的点分成不同的批次,不同的类型,也就是分层。
一般来说,分层图就是原来存在的节点乘上某个数再加上原来的节点,直接一点,就是变成一个不会重复的,空间合理的,有顺序的其他节点进行建图。
P4568 [JLOI2011] 飞行路线
题目描述
Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在
Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多
数据规模与约定
对于
对于
对于
因为免费的次数是有限的,所以我们将一次免费的乘坐看作是上了一层楼,如图。
建图核心代码
rep(i, 1, m){ int a, b, c; cin >> a >> b >> c; add(a, b, c);//本层 add(b, a, c); rep(j, 1, k){ add(a + (j - 1) * n, b + j * n, 0);//j只能从1开始,不然越界,a到b免费 add(b + (j - 1) * n, a + j * n, 0);//b也可以建向a add(a + j * n, b + j * n, c);//这一层可以不使用免费次数 add(b + j * n, a + j * n, c);//同理 } }
Dijkstra 还是 SPFA 自己选,反正最短路很简单,但是输出答案又有细节。
取答案核心代码
ans = 0x3f3f3f3f; rep(i, 0, k) ans = min(ans, dis[t + i * n]);//可能用不完
在定义数组是必须注意计算清楚,开几倍空间,是否开 LL
等等。
例如这道题,一次机会会产生
const int N = 1e4 + 10, M = 5e4 + 10, K = 15, E = (M * K) << 2;//必须开4倍,会有4条边产生
边数
另外这两道题也不尽相同:P2939 [USACO09FEB] Revamping Trails G,P4822 [BJWC2012] 冻结,冻结需要让边长除以二,但是基本相同。
P5340 [TJOI2019] 大中锋的游乐场
题目描述
大中锋正在一个游乐场里玩耍。游乐场里有
由于大中锋十分贪吃,所以每当他走到一个娱乐设施,他都会先去购买一杯可乐或一个汉堡,并把它们吃掉。但如果大中锋吃掉的汉堡数量比他喝掉的可乐数量多于
现在大中锋正在第
注意:大中锋非常贪吃,所以他到达每个点的第一件事是去吃(或者喝),才考虑其他的事情,所以在起始点和终点他都会去买汉堡(可乐),你也需要保证在这两个点他不会感到很饿或者很渴。
题目补充说明
- 路径不一定是简单路径。
- 大中锋可以多次经过一个节点,同时每次都会取得汉堡/可乐。
注意到
还有几点需要注意到,由于起点的点权也要加上,所以需要对起点的点权类型进行特判。多测需要清空。
# include <bits/stdc++.h> # define sec second # define mem(a, b) memset(a, b, sizeof(a)) using namespace std; typedef pair<int, int> PII; const int N = 9e5, M = 5e6; int T, n, m, k, s, t, idx; int op[N], dis[M]; int h[M], e[M << 1], w[N << 1], ne[M << 1]; bool vis[M]; int getid(int pos, int f){ return n * (10 + f) + pos; } void add(int a, int b, int c){ e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++; } void dijkstra(int start){ mem(dis, 0x3f); mem(vis, false); priority_queue<PII, vector<PII>, greater<PII> > heap; dis[start] = 0; heap.push({0, start}); while(!heap.empty()){ int u = heap.top().sec; heap.pop(); if(vis[u]) continue; for(int i = h[u]; ~i; i = ne[i]){ int v = e[i]; if(vis[v] || dis[v] <= dis[u] + w[i]) continue; dis[v] = dis[u] + w[i]; heap.push({dis[v], v}); } vis[u] = true; } } int main(){ cin >> T; while(T --){ idx = 0; mem(h, -1); cin >> n >> m >> k; for(int i = 1; i <= n; i ++){ cin >> op[i]; } for(int i = 1, a, b, c; i <= m; i ++){ cin >> a >> b >> c; for(int j = -k; j <= k; j ++){ if(j + 1 <= k && op[b] == 1) add(getid(a, j), getid(b, j + 1), c); if(j - 1 >= -k && op[b] == 2) add(getid(a, j), getid(b, j - 1), c); if(j + 1 <= k && op[a] == 1) add(getid(b, j), getid(a, j + 1), c); if(j - 1 >= -k && op[a] == 2) add(getid(b, j), getid(a, j - 1), c); } } cin >> s >> t; dijkstra(getid(s, op[s] == 1 ? 1 : -1)); int ans = 0x3f3f3f3f; for(int j = -k; j <= k; j ++) ans = min(ans, dis[getid(t, j)]); if(ans == 0x3f3f3f3f) cout << -1 << "\n"; else cout << ans << "\n"; } return 0; }
現代的な屋敷 (Modern Mansion)
题面翻译
有一座东西
任何相邻的两间房之间都有一扇门连接,若一扇门是打开状态,则可以从门的一边走到另一边,并且花费
最开始连接东西相邻房间的所有门都关闭,连接南北相邻房间的所有门都打开,输出从房间
(续写
集训的时候做的一道题。
对于同一个有开关房间,它有南北开通和东西开通两种状态,并且这两种状态相互转换需要
本文作者:MrTourist
本文链接:https://www.cnblogs.com/MrTourist/p/18515887
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步