首页 > 其他分享 >[JOI 2013 Final]现代豪宅

[JOI 2013 Final]现代豪宅

时间:2024-10-10 15:45:13浏览次数:1  
标签:get int 边权 id 按钮 JOI Final 2013 dis

[JOI 2013 Final]现代豪宅

题意

给出一个 \(n\times m\) 的网格图,每两个格子之间有一扇门。

初始上下方向的门都是开着的,左右方向的门是关着的。

有一些格子有按钮,可以把打开的门关上,关上的门打开。

走一步需要一秒,按按钮需要一秒,求从 \((1,1)\) 到达 \((n,m)\) 的最小步数。

思路

发现题意等价于只能在按钮处拐弯。

只用把起点,终点还有每个按钮建立成点,连边跑最短路。

同一行同一列的点只用相邻的连边,边权为两点距离。

起点只用和第一列的第一个按钮连边,边权同理。

终点只用和最后一列最后一个按钮和最后一行最后一个按钮连边,边权同理。

那按按钮需要花费一秒,我们需要在按钮内部建出一条边权为 \(1\) 的点,怎么办呢?

把一个按钮拆成 \(3\) 个点,分别问行出入口,列出入口,中转站。

行出入口向中转站连一条边权为 \(1\) 的边,

列出入口向中转站连一条边权为 \(1\) 的边,

中转站向行出入口和列出入口连边权为 \(0\) 的边。

(可以想象成坐地铁,进站要给钱,出站不用)

同一列的点列出入口相互连接,

同一行的点行出入口相互连接。

这样就与题目的网格图等价,跑最短路就行了。

代码

#include <bits/stdc++.h>
#define MIDDLE 0
#define LEFT_RIGHT 1
#define UP_DOWN 2
#define S_T 4
using namespace std;

const int N = 2e6 + 5;
using LL = long long;

int tot, head[N], ver[N * 2], nxt[N * 2];
LL edge[N * 2], dis[N];

map <pair <int, pair<int, int>>, int> id;
vector <int> h[N], l[N];

int n, m, k, cnt;
struct Button {
	int x, y;
};
Button b[N];

struct node {
	int id; LL dis;
};

bool operator < (node x, node y) {
	return x.dis > y.dis;
}

priority_queue <node> Q;
bool vis[N];

void add(int x, int y, LL z) {
	ver[++ tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
	edge[tot] = z;
}

int get_id(int x, int y, int t) {
	if (id.find({x, {y, t}}) != id.end())
		return id[{x, {y, t}}];
	id[{x, {y, t}}] = ++ cnt;
	return cnt;
}

void dijkstra(int s) {
	memset(dis, 0x3f, sizeof(dis));
	dis[s] = 0;
	Q.push({s, 0});
	while (!Q.empty()) {
		int x = Q.top().id; Q.pop();
		if (vis[x]) continue;
		vis[x] = 1;
		for (int i = head[x], y; i; i = nxt[i]) {
			y = ver[i];
			if (dis[y] > dis[x] + edge[i]) {
				dis[y] = dis[x] + edge[i];
				Q.push({y, dis[y]});
			}
		}
	}
}

int main() {
	freopen("house.in", "r", stdin);
	freopen("house.out", "w", stdout); 
	
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	
	cin >> m >> n >> k;
	
	int S = get_id(1, 1, S_T), T = get_id(m, n, S_T);
	
	for (int i = 1; i <= k; i ++) {
		cin >> b[i].y >> b[i].x;
		h[b[i].x].push_back(b[i].y);
		l[b[i].y].push_back(b[i].x);
		int x = get_id(b[i].x, b[i].y, MIDDLE);
		int y = get_id(b[i].x, b[i].y, LEFT_RIGHT);
		int z = get_id(b[i].x, b[i].y, UP_DOWN);
		add(y, x, 1); add(x, y, 0);
		add(z, x, 1); add(x, z, 0);
	}
	
	for (int i = 1; i <= n; i ++) {
		if (!h[i].size()) continue;
		sort(h[i].begin(), h[i].end());
		for (size_t j = 0; j < h[i].size() - 1; j ++) {
			int x = get_id(i, h[i][j], LEFT_RIGHT);
			int y = get_id(i, h[i][j + 1], LEFT_RIGHT);
			add(x, y, h[i][j + 1] - h[i][j]);
			add(y, x, h[i][j + 1] - h[i][j]);
		}
	}
	
	for (int i = 1; i <= m; i ++) {
		if (!l[i].size()) continue;
		sort(l[i].begin(), l[i].end());
		for (size_t j = 0; j < l[i].size() - 1; j ++) {
			int x = get_id(l[i][j], i, UP_DOWN);
			int y = get_id(l[i][j + 1], i, UP_DOWN);
			add(x, y, l[i][j + 1] - l[i][j]);
			add(y, x, l[i][j + 1] - l[i][j]);
		}
	}
	
	if (l[1].size()) {
		int temp = get_id(l[1][0], 1, UP_DOWN);
		add(S, temp, l[1][0] - 1);
		add(temp, S, l[1][0] - 1);
	}
	
	
	if (h[n].size()) {
		int temp = get_id(n, h[n].back(), LEFT_RIGHT);
		add(T, temp, m - h[n].back());
		add(temp, T, m - h[n].back());
	}
	
	if (l[m].size()) {
		int temp = get_id(l[m].back(), m, UP_DOWN);
		add(T, temp, n - l[m].back());
		add(temp, T, n - l[m].back());
	}
	
	dijkstra(S);
	
	if (dis[T] ==	dis[0]) cout << -1 << "\n";
	else cout << dis[T] << "\n";
	return 0;
}

标签:get,int,边权,id,按钮,JOI,Final,2013,dis
From: https://www.cnblogs.com/maniubi/p/18456513

相关文章

  • [JOI 2013 Final]搭乘 IOI 火车
    [JOI2013Final]搭乘IOI火车题意给出两个由\(\text{OI}\)组成的字符串\(S,T\)。可以删除每个字符串的前缀和后缀。每次从剩下部分的第一位取出一个字符放到新的字符串中。要求新字符串必须以\(\text{I}\)开头结尾,相同的字符不能相邻,求新字符串的最大长度。思路定义......
  • Guava中的Joiner和Splitter
    目录Guava介绍Joinerlist转stringmap转string处理嵌套集合处理null值Splitterstring转liststring转map多个拆分符输出代码Guava介绍Guava是Google开发的一个开源Java库,提供一系列核心功能增强Java的标准库。它包含许多有用的工具和集合类,使Java开发更加高效,代码更加......
  • gjoi 2024.10.9
    当天在家里躺尸看t1过不了就去睡觉了,还好没写卡场Round哦/cf怎么有人吃错了一整盒退高烧药啊/wqT1游戏升级考虑有多少\(x\in[1,n]\)满足\(b_1+\lfloor\frac{a_1}{x}\rfloor=b_2+\lfloor\frac{a_2}{x}\rfloor\),直接对下取整做整除分块即可。gjoj卡常所以开longl......
  • [ZJOI2008] 骑士
    \(基环树DP\)https://www.luogu.com.cn/problem/P2607\(将基环树上面的环破开成树就能进行如同《没有上司的舞会》的树形DP\)\(没有上司的舞会:\)https://www.luogu.com.cn/problem/P1352\(具体实现困难之处在于如何破环成树,其实只需要主要到对于树上的一个环,将环上的两个节......
  • JOI Open 2018
    T1BubbleSort2题意:给定一个长度为\(n\)的序列\(a\),进行\(q\)次修改,第\(i\)次将第\(x_i\)个元素的值修改为\(y_i\)。对于每次操作后,你都需要求出,如果此时对序列进行冒泡排序,需要多少次冒泡才能完成排序。\(n\le5\times10^5\)。序列有序意味着,每个数前面都没......
  • mysql join语法解析
     MySQL支持以下JOIN语法用于SELECT语句和多表DELETE和UPDATE语句中的table_references部分:table_references:查询中涉及的一个或多个表的引用,可以是简单表名或JOIN表达式的组合。escaped_table_reference[,escaped_table_reference]...escaped_table_ref......
  • 了解final关键字在Java并发编程领域的作用吗?
    在Java并发编程领域,final关键字扮演着一个至关重要的角色。虽然很多同学熟悉final用于修饰变量、方法和类的基本用法,但其在并发环境中的应用和原理却常常被忽视。final关键字不仅仅是一个简单的修饰符,它在多线程编程中确保对象状态的可见性和不变性,这对于构建线程安全的应用至关重......
  • Codeforces Round 977 (Div. 2, based on COMPFEST 16 - Final Round)
    目录写在前面A签到B贪心,枚举C1贪心C2贪心,枚举DDPE1/E2Kruscal重构树,树上背包写在最后写在前面补题地址:https://codeforces.com/contest/2021。上大分失败呃呃呃呃我不要上班呜呜A签到考虑仅有三个数\(a,b,c(a<b<c)\)时最优操作,手玩下发现最优操作顺序一定是......
  • 使用try-with-resource 的情况下,resource 进入catch 块或者 finally 块之前,已经关闭了
    在Java中,使用try-with-resources的情况下,资源会在try块执行完毕后自动关闭。具体来说,无论是否发生异常,资源总是在控制流进入catch或finally块之前关闭。关键点:try-with-resources是在try语句中声明和管理实现了AutoCloseable接口的资源,例如InputStream、Output......
  • 使用apatch httpClient, 并且我用了try-with-resource, 我希望在catch 和 finally 中从
    在使用ApacheHttpClient时,如果你使用了try-with-resources语法并希望在catch或finally块中从response对象中读取完整的responseentity,你可能会遇到资源过早关闭的问题。这是因为try-with-resources会在try块结束后自动关闭资源,导致在catch或finally块中无法......